Logo Search packages:      
Sourcecode: eggcups version File versions  Download package

ec-tray-icon.c

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
/*
 * Copyright (C) 2002 CodeFactory AB
 * Copyright (C) 2002 Richard Hult <rhult@codefactory.se>
 * Copyright (C) 2002, 2003, 2004 Tim Waugh <twaugh@redhat.com>
 * Copyright (C) 2004 Red Hat, Inc.
 * Rewritten by Colin Walters <walters@redhat.com>
 *
 * 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, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 */

#define EC_OBJECT_PATH "/com/redhat/PrinterManager"
#define EC_CONF_SESSION_CUPSD "/apps/eggcups/session_cupsd"

#include "ec-tray-icon.h"
#include "config.h"
#include "ec-job-model.h"
#include "ec-job-list.h"
#include "ec-cups-manager.h"
#include "rb-debug.h"
#include "ec-driver-prompt.h"
#include <dbus/dbus.h>
#include <dbus/dbus-glib-lowlevel.h>
#include "eggtrayicon.h"
#include <errno.h>
#include <signal.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <time.h>
#include <unistd.h>
#include <gdk/gdk.h>
#include <gdk/gdkkeysyms.h>
#include <gtk/gtk.h>
#include <gconf/gconf.h>
#include <gconf/gconf-client.h>
#include <libgnomeui/gnome-stock-icons.h>
#include <libgnomeui/gnome-password-dialog.h>
#include <libgnome/gnome-i18n.h>
#include <libgnomecups/gnome-cups-printer.h>
#include <gnome-keyring.h>

/* Currently disabled; this way, the icon's visibility is per-session
 * basically.
 */
#define DISPLAY_TIMEOUT_SECONDS 0

#define BLINK_DURATION_SECONDS 2
#define BLINK_CHANGE_SECONDS 0.5

G_DEFINE_TYPE(ECTrayIcon, ec_tray_icon, EGG_TYPE_TRAY_ICON)

00068 struct ECTrayIconBlinkContext
{
      ECTrayIcon *icon;
      gboolean blanked;
      guint count;
};

static void ec_tray_icon_dispose (GObject *object);
static void ec_tray_icon_finalize (GObject *object);

static gboolean idle_check_system_dbus (ECTrayIcon *icon);
static gboolean idle_check_session_dbus (ECTrayIcon *icon);
static char * item_factory_trans_cb (const gchar *path, gpointer data);
static void popup_about_cb (gpointer callback_data, guint action, GtkWidget *widget);
static void menu_hide_cb (ECTrayIcon *icon, guint action, GtkWidget *widget);
static gboolean icon_button_press_cb (GtkWidget *widget, GdkEventButton *event, ECTrayIcon *icon);
static void icon_destroy_cb (GtkWidget *widget, gpointer user_data);
static gboolean sync_state (ECTrayIcon *icon);
static gboolean idle_blink (struct ECTrayIconBlinkContext *ctxt);
static void idle_blink_ctxt_destroy (gpointer data);
static gboolean job_visible_func (GtkTreeModel *model, GtkTreeIter *iter, ECTrayIcon *icon);
static void row_inserted_cb (GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, ECTrayIcon *icon);
static void row_deleted_cb (GtkTreeModel *model, GtkTreePath *path, ECTrayIcon *icon);
static void row_changed_cb (GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, ECTrayIcon *icon);
static void job_state_changed_cb (ECJobModel *model, gchar *printer, gint old_state, GnomeCupsJob *job, gpointer user_data);
static void ec_dbus_unregister_handler (DBusConnection *connection, void *data);
static DBusHandlerResult ec_dbus_message_handler (DBusConnection *connection, DBusMessage *message, void *user_data);

#define GIF_CB(x) ((GtkItemFactoryCallback)(x))

static GtkItemFactoryEntry popup_items[] = {
      { N_("/_About"), NULL, GIF_CB (popup_about_cb), 0,
        "<StockItem>",  GNOME_STOCK_ABOUT },
      { "/sep1", NULL, 0, 0, "<Separator>",  NULL },
      { N_("/_Hide"), "", GIF_CB (menu_hide_cb), 0, NULL },
};

static DBusObjectPathVTable
ec_vtable = { &ec_dbus_unregister_handler,
            &ec_dbus_message_handler,
            NULL,
            NULL,
            NULL,
            NULL };

00113 struct ECTrayIconPrivate {
      gboolean disposed;
      
      /* D-BUS */
      DBusConnection *system_dbus;
      DBusConnection *session_dbus;

      char *username;

      /* Widgets. */
      GtkWidget *ebox;
      GtkWidget *icon_image;
      GdkPixbuf *jobs_pixbuf;
      GdkPixbuf *blank_pixbuf;

#ifdef WITH_SESSION_CUPSD
      ECCupsManager *cups_manager;
#endif

      ECJobModel *model;
      GtkTreeModel *filtered_model;

      ECJobList *list_window;

      GtkItemFactory *popup_factory;
      GtkTooltips *tips;

      guint idle_check_system_dbus_id;
      guint idle_check_session_dbus_id;
      guint idle_hide_id;
      guint idle_blink_id;

      ECDriverPrompt *driverprompt;
};

static GObjectClass *parent_class = NULL;

static void
ec_tray_icon_class_init (ECTrayIconClass *klass)
{
      GObjectClass *object_class = G_OBJECT_CLASS (klass);

      parent_class = g_type_class_peek_parent (klass);

      object_class->dispose = ec_tray_icon_dispose;
      object_class->finalize = ec_tray_icon_finalize;
}

void
ec_tray_icon_init (ECTrayIcon *icon)
{
      char *fname;
      
      icon->priv = g_new0 (struct ECTrayIconPrivate, 1);

      icon->priv->popup_factory = gtk_item_factory_new (GTK_TYPE_MENU,
                                      "<main>",
                                      NULL);
      gtk_item_factory_set_translate_func (icon->priv->popup_factory,
                                   item_factory_trans_cb,
                                   NULL,
                                   NULL);

      gtk_item_factory_create_items (icon->priv->popup_factory,
                               G_N_ELEMENTS (popup_items),
                               popup_items,
                               icon);

      icon->priv->ebox = gtk_event_box_new ();
      g_signal_connect_object (G_OBJECT (icon->priv->ebox),
                         "button_press_event",
                         G_CALLBACK (icon_button_press_cb),
                         icon, 0);

      fname = g_build_filename (IMAGEDIR, "icon.png", NULL);
      icon->priv->jobs_pixbuf = gdk_pixbuf_new_from_file (fname, NULL);
      g_free (fname);
      
      icon->priv->icon_image = gtk_image_new_from_pixbuf (icon->priv->jobs_pixbuf);
      gtk_container_add (GTK_CONTAINER (icon->priv->ebox), icon->priv->icon_image);

      icon->priv->blank_pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8,
                                       gdk_pixbuf_get_width (icon->priv->jobs_pixbuf),
                                       gdk_pixbuf_get_height (icon->priv->jobs_pixbuf));
      gdk_pixbuf_fill (icon->priv->blank_pixbuf, 0);

      gtk_container_add (GTK_CONTAINER (icon), icon->priv->ebox);

      icon->priv->tips = gtk_tooltips_new ();

      g_signal_connect_object (G_OBJECT (icon),
                         "destroy",
                         G_CALLBACK (icon_destroy_cb),
                         icon, 0);

      icon->priv->username = g_strdup (g_get_user_name ());

      icon->priv->model = ec_job_model_new ();
      icon->priv->filtered_model = gtk_tree_model_filter_new (GTK_TREE_MODEL (icon->priv->model), NULL);
      g_signal_connect_object (G_OBJECT (icon->priv->filtered_model),
                         "row_inserted",
                         G_CALLBACK (row_inserted_cb),
                         icon, 0);
      g_signal_connect_object (G_OBJECT (icon->priv->filtered_model),
                         "row_deleted",
                         G_CALLBACK (row_deleted_cb),
                         icon, 0);
      g_signal_connect_object (G_OBJECT (icon->priv->filtered_model),
                         "row_changed",
                         G_CALLBACK (row_changed_cb),
                         icon, 0);
      g_signal_connect_object (G_OBJECT (icon->priv->model),
                         "state_changed",
                         G_CALLBACK (job_state_changed_cb),
                         NULL, 0);
      gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (icon->priv->filtered_model),
                                    (GtkTreeModelFilterVisibleFunc) job_visible_func,
                                    icon, NULL);

      icon->priv->system_dbus = NULL;
      icon->priv->idle_check_system_dbus_id = g_idle_add ((GSourceFunc) idle_check_system_dbus,
                                              icon);
      icon->priv->session_dbus = NULL;
      icon->priv->idle_check_session_dbus_id = g_idle_add ((GSourceFunc) idle_check_session_dbus,
                                               icon);
      g_idle_add ((GSourceFunc) sync_state, icon);
}

static void
ec_tray_icon_dispose (GObject *object)
{
      ECTrayIcon *icon = EC_TRAY_ICON (object);

      if (icon->priv->disposed)
            return;
      icon->priv->disposed = TRUE;

      rb_debug ("disposing");
      if (icon->priv->system_dbus) {
            dbus_connection_close (icon->priv->system_dbus);
            dbus_connection_unref (icon->priv->system_dbus);
            icon->priv->system_dbus = NULL;
      }

      if (icon->priv->session_dbus) {
            dbus_connection_close (icon->priv->session_dbus);
            dbus_connection_unref (icon->priv->session_dbus);
            icon->priv->session_dbus = NULL;
      }

      if (icon->priv->idle_check_system_dbus_id) {
            g_source_remove (icon->priv->idle_check_system_dbus_id);
            icon->priv->idle_check_system_dbus_id = 0;
      }

      if (icon->priv->idle_check_session_dbus_id) {
            g_source_remove (icon->priv->idle_check_session_dbus_id);
            icon->priv->idle_check_session_dbus_id = 0;
      }

      if (icon->priv->idle_blink_id > 0) {
            g_source_remove (icon->priv->idle_blink_id);
            icon->priv->idle_blink_id = 0;
      }

#ifdef WITH_SESSION_CUPSD
      if (icon->priv->cups_manager)
            g_object_unref (icon->priv->cups_manager);
#endif

      if (icon->priv->list_window)
            g_object_unref (icon->priv->list_window);

      g_object_unref (G_OBJECT (icon->priv->jobs_pixbuf));
      g_object_unref (G_OBJECT (icon->priv->blank_pixbuf));
      gtk_object_destroy (GTK_OBJECT (icon->priv->tips));

      g_object_unref (G_OBJECT (icon->priv->model));
}

static void
ec_tray_icon_finalize (GObject *object)
{
      ECTrayIcon *icon = EC_TRAY_ICON (object);

      g_return_if_fail (icon->priv != NULL);

      rb_debug ("finalizing!");

      g_free (icon->priv->username);

      g_free (icon->priv);

      G_OBJECT_CLASS (ec_tray_icon_parent_class)->finalize (object);
}
       
ECTrayIcon *
ec_tray_icon_new (void)
{
        return EC_TRAY_ICON (g_object_new (EC_TYPE_TRAY_ICON, "title", "EggCups", NULL));
}

static void
menu_hide_cb (ECTrayIcon *icon,
            guint action,
            GtkWidget *widget)
{
      rb_debug ("hiding icon");

      gtk_widget_hide (GTK_WIDGET (icon));

      if (icon->priv->idle_hide_id) {
            g_source_remove (icon->priv->idle_hide_id);
            icon->priv->idle_hide_id = 0;
      }
}

static void
popup_about_cb (gpointer callback_data,
            guint action,
            GtkWidget *widget)
{
      ec_tray_icon_show_about_window ();
}     

void
ec_tray_icon_show_about_window (void)
{
#if GTK_CHECK_VERSION(2,6,0)
      static const gchar *authors[] = {
            "Tim Waugh <twaugh@redhat.com>",
            "Colin Walters <walters@redhat.com>",
            "Matthias Clasen <mclasen@redhat.com>",
            NULL
      };

      gchar *copyright = g_strjoin ("\n",
                                    _("Copyright \xc2\xa9 2002 Tim Waugh."),
                                    _("Copyright \xc2\xa9 2004 Colin Walters."),
                                    _("Copyright \xc2\xa9 2004 Matthias Clasen."),
                                    _("Based on code by CodeFactory."),
                                    NULL);

      gtk_show_about_dialog (NULL,
                             "name", _("Print Notifier"),
                             "version", VERSION,
                             "comments", _("A program for print job notification."),
                             "copyright", copyright,
                             "authors", authors,
                             NULL);
      g_free (copyright);
#else
      static GtkWidget *about_window;
      GtkWidget *vbox;
      GtkWidget *label;
      gchar *markup;

      if (about_window) {
            gtk_window_present (GTK_WINDOW (about_window));
            return;
      }
      
      about_window = gtk_dialog_new ();

      g_object_add_weak_pointer (G_OBJECT (about_window),
                           (gpointer *) &about_window);
      
      gtk_dialog_add_button (GTK_DIALOG (about_window),
                         GTK_STOCK_OK, GTK_RESPONSE_OK);
      gtk_dialog_set_default_response (GTK_DIALOG (about_window),
                               GTK_RESPONSE_OK);
      g_signal_connect (about_window, "response",
                    G_CALLBACK (gtk_widget_hide), NULL);

      gtk_window_set_title (GTK_WINDOW (about_window), _("About Print Notifier"));
      
      gtk_window_set_resizable (GTK_WINDOW (about_window), FALSE);
      gtk_window_set_position (GTK_WINDOW (about_window), 
                         GTK_WIN_POS_CENTER_ON_PARENT);
      gtk_window_set_type_hint (GTK_WINDOW (about_window), 
                          GDK_WINDOW_TYPE_HINT_DIALOG);

      vbox = gtk_vbox_new (FALSE, 0);
      gtk_container_set_border_width (GTK_CONTAINER (vbox), 6);
      gtk_box_pack_start (GTK_BOX (GTK_DIALOG (about_window)->vbox),
                      vbox, FALSE, FALSE, 0);

      label = gtk_label_new (NULL);
      gtk_misc_set_alignment (GTK_MISC (label), 0.5, 0.5);
      gtk_label_set_justify (GTK_LABEL (label), GTK_JUSTIFY_CENTER);
      markup = g_strdup_printf ("<span size=\"xx-large\" weight=\"bold\">%s " VERSION "</span>\n\n"
                          "%s\n\n"
                          "<span size=\"small\">%s</span>\n"
                          "<span size=\"small\">%s</span>\n"
                          "<span size=\"small\">%s</span>\n"
                          "<span size=\"small\">%s</span>\n",
                          _("Print Notifier"),
                          _("A program for print job notification."),
                          ("Copyright \xc2\xa9 2002 Tim Waugh &lt;twaugh@redhat.com&gt;"),
                          ("Copyright \xc2\xa9 2004 Colin Walters &lt;walters@redhat.com&gt;"),
                          ("Copyright \xc2\xa9 2004 Matthias Clasen &lt;mclasen@redhat.com&gt;"),
                          _("Based on code by CodeFactory."));
      gtk_label_set_markup (GTK_LABEL (label), markup);
      g_free (markup);
      gtk_box_pack_start (GTK_BOX (vbox), label, TRUE, TRUE, 0);
      
      gtk_widget_show_all (about_window);
      gtk_dialog_run (GTK_DIALOG (about_window));
      gtk_widget_hide (about_window);
#endif
}

static DBusHandlerResult 
handle_dbus_local_queue (ECTrayIcon *icon,
                   gboolean is_session,
                   DBusMessage *message)
{
      DBusHandlerResult res = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
      const gchar *printer_name = NULL, *username = NULL;
      dbus_uint32_t local_job_id;
      DBusError error;

      dbus_error_init (&error);
      if (!dbus_message_get_args (message, &error,
                            DBUS_TYPE_STRING, &printer_name,
                            DBUS_TYPE_UINT32, &local_job_id,
                            DBUS_TYPE_STRING, &username,
                            DBUS_TYPE_INVALID)) {
            rb_debug ("Couldn't parse arguments");
            goto out;
      }

      if ((username != NULL) && 
            (strcmp (icon->priv->username, username) != 0)) {
            rb_debug ("My username %s doesn't match: %s",
                    icon->priv->username, username);
            goto out;
      }

      if (is_session)
            local_job_id ^= (1 << 31);
            
      rb_debug ("eggcups: job %d locally queued for printer %s", local_job_id, printer_name);
      ec_job_model_job_submitted_local (icon->priv->model, printer_name, local_job_id);

      res = DBUS_HANDLER_RESULT_HANDLED;
 out:
      return res;
}

static DBusHandlerResult 
handle_dbus_local_start (ECTrayIcon *icon,
                   gboolean is_session,
                   DBusMessage *message)
{
      DBusHandlerResult res = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
      const gchar *printer_name = NULL;
      dbus_uint32_t local_job_id;
      const gchar *username = NULL;
      DBusError error;

      dbus_error_init (&error);
      if (!dbus_message_get_args (message, &error,
                            DBUS_TYPE_STRING, &printer_name,
                            DBUS_TYPE_UINT32, &local_job_id,
                            DBUS_TYPE_STRING, &username,
                            DBUS_TYPE_INVALID)) {
            rb_debug ("Couldn't parse arguments");
            goto out;
      }

      if ((username != NULL) &&
            (strcmp (icon->priv->username, username) != 0)) {
            rb_debug ("My username %s doesn't match: %s",
                    icon->priv->username, username);
            goto out;
      }

      if (is_session)
            local_job_id ^= (1 << 31);
            
      rb_debug ("eggcups: job %d has started printing for local printer %s", local_job_id, printer_name);
      ec_job_model_job_started_local (icon->priv->model, printer_name, local_job_id);

      res = DBUS_HANDLER_RESULT_HANDLED;
 out:
      return res;
}

static DBusHandlerResult 
handle_dbus_remote_queue (ECTrayIcon *icon,
                    gboolean is_session,
                    DBusMessage *message)
{
      DBusHandlerResult res = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
      const gchar *remote_printer_uri = NULL;
      const gchar *submission_status = NULL;
      const gchar *username = NULL;
      const gchar *printer_name = NULL;
      guint32 local_job_id;
      guint32 remote_job_id;
      DBusError error;

      dbus_error_init (&error);
      if (!dbus_message_get_args (message, &error,
                            DBUS_TYPE_STRING, &remote_printer_uri,
                            DBUS_TYPE_STRING, &submission_status,
                            DBUS_TYPE_UINT32, &local_job_id,
                            DBUS_TYPE_UINT32, &remote_job_id,
                            DBUS_TYPE_STRING, &username,
                            DBUS_TYPE_STRING, &printer_name,
                            DBUS_TYPE_INVALID)) {
            rb_debug ("Couldn't parse arguments");
            goto out;
      }

      if ((username != NULL) &&
            (strcmp (icon->priv->username, username) != 0)) {
            rb_debug ("My username %s doesn't match: %s",
                    icon->priv->username, username);
            goto out;
      }

      if (is_session)
            local_job_id ^= (1 << 31);

      if ((submission_status != NULL) && 
            ((strcmp (submission_status, "server-error-temporary-error") == 0) || 
             (strcmp (submission_status, "server-error-not-accepting-jobs") == 0) ||
           (strcmp (submission_status, "server-error-busy") == 0))) {
            res = DBUS_HANDLER_RESULT_HANDLED;
            rb_debug ("caught temporary error %s", submission_status);
            goto out;
      } else {
            rb_debug ("remote job %d (local %d) submitted to %s (%s): %s",
                    remote_job_id, local_job_id,
                    printer_name,
                    remote_printer_uri,
                    submission_status);
            ec_job_model_job_sent_remote (icon->priv->model,
                                    printer_name,
                                    local_job_id,
                                    remote_printer_uri,
                                    remote_job_id);
      }

      sync_state (icon);

      res = DBUS_HANDLER_RESULT_HANDLED;
 out:
      return res;
}

static DBusHandlerResult
handle_generic_dbus_message (ECTrayIcon *icon,
                       gboolean is_session,
                       DBusConnection *connection,
                       DBusMessage *message)
{
      DBusHandlerResult res = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;

      GDK_THREADS_ENTER ();

      if (dbus_message_is_signal (message,
                            "com.redhat.PrinterSpooler",
                            "JobQueuedLocal")) {
            res = handle_dbus_local_queue (icon, is_session, message);
      } else if (dbus_message_is_signal (message,
                            "com.redhat.PrinterSpooler",
                            "JobStartedLocal")) {
            res = handle_dbus_local_start (icon, is_session, message);
      } else if (dbus_message_is_signal (message,
                                 "com.redhat.PrinterSpooler",
                                 "JobQueuedRemote")) {
            res = handle_dbus_remote_queue (icon, is_session, message);
      } else if (dbus_message_is_signal (message,
                            DBUS_INTERFACE_LOCAL,
                            "Disconnected")) {
            rb_debug ("DISCONNECTED from %s bus!",
                    is_session ? "session" : "system");
            exit (0);
      }

      GDK_THREADS_LEAVE ();

      return res;
}

static DBusHandlerResult
handle_system_dbus_message (DBusConnection     *connection,
                       DBusMessage        *message,
                       void               *user_data)
{
      ECTrayIcon *icon = EC_TRAY_ICON (user_data);

      rb_debug ("received system bus message: %s.%s",
              dbus_message_get_interface (message),
              dbus_message_get_member (message));

      return handle_generic_dbus_message (icon,
                                  FALSE, connection,
                                  message);
}

static DBusHandlerResult
handle_session_dbus_message (DBusConnection     *connection,
                       DBusMessage        *message,
                       void               *user_data)
{
      ECTrayIcon *icon = EC_TRAY_ICON (user_data);

      rb_debug ("received session bus message: %s.%s",
              dbus_message_get_interface (message),
              dbus_message_get_member (message));

      return handle_generic_dbus_message (icon, TRUE,
                                  connection, message);
}

static void
ec_dbus_unregister_handler (DBusConnection *connection, void *data)
{
      rb_debug ("unregistered");
}

static char *
lookup_password_for_printer (const char *printer_name)
{
      GnomeKeyringResult keyring_result;
      GnomeKeyringAttributeList *attributes = NULL;
      char *ret = NULL;
      GList *found_items = NULL;
      
      attributes = gnome_keyring_attribute_list_new ();
      gnome_keyring_attribute_list_append_string (attributes,
                                        "ipp-printer-basic-password",
                                        printer_name);
      keyring_result = gnome_keyring_find_items_sync (GNOME_KEYRING_ITEM_GENERIC_SECRET,
                                          attributes,
                                          &found_items);
      if (keyring_result == GNOME_KEYRING_RESULT_OK
          && found_items != NULL) {
            ret = g_strdup (((GnomeKeyringFound *)found_items->data)->secret);
            rb_debug ("found stored password %s", ret);
      }
      gnome_keyring_attribute_list_free (attributes);
      return ret;
}

static void
save_password_for_printer (const char *printer_name, const char *keyring,
                     const char *password)
{
      gchar *nice_name = g_strdup_printf (_("Password for printer \"%s\""), printer_name);
      guint32 item_id;
      GnomeKeyringResult keyring_result;
      GnomeKeyringAttributeList *attributes;

      attributes = gnome_keyring_attribute_list_new ();
      gnome_keyring_attribute_list_append_string (attributes,
                                        "ipp-printer-basic-password",
                                        printer_name);
      keyring_result = gnome_keyring_item_create_sync (keyring,
                                           GNOME_KEYRING_ITEM_GENERIC_SECRET,
                                           nice_name,
                                           attributes,
                                           password,
                                           TRUE,
                                           &item_id);
      gnome_keyring_attribute_list_free (attributes);
      g_free (nice_name);
      if (keyring_result != GNOME_KEYRING_RESULT_OK) {
            GtkWidget *dialog;
            dialog = gtk_message_dialog_new (NULL,
                                     GTK_DIALOG_DESTROY_WITH_PARENT,
                                     GTK_MESSAGE_ERROR,
                                     GTK_BUTTONS_CLOSE,
                                     _("Couldn't store password in keyring"));
            gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
                                            _("Got error code %d while trying to store password"),
                                            (int) keyring_result);
            gtk_dialog_run (GTK_DIALOG (dialog));
      }
}

static DBusHandlerResult
ec_dbus_message_handler (DBusConnection *connection,
                   DBusMessage *message,
                   void *user_data)
{
      rb_debug ("got message");

      if (dbus_message_is_method_call (message,
                               "com.redhat.PrinterManager",
                               "promptPassword")) {
            DBusError error;
            DBusMessage *reply;
            char *password;
            char *prompt;
            gboolean use_stored_password;
            gboolean got_error;
            const gchar *printer_name;
            GnomePasswordDialogRemember remember;
            GnomePasswordDialog *dialog;
      
            rb_debug ("entering promptPassword");

            dbus_error_init (&error);
            if (!dbus_message_get_args (message, &error,
                                  DBUS_TYPE_STRING, &printer_name,
                                  DBUS_TYPE_BOOLEAN, &use_stored_password,
                                  DBUS_TYPE_INVALID)) {
                  g_warning ("Couldn't parse arguments to promptPassword!");
                  return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
            }

            prompt = g_strdup_printf (_("Enter password for printer \"%s\""),
                                printer_name);

            if (use_stored_password)
                  password = lookup_password_for_printer (printer_name);
            else
                  password = NULL;

            got_error = FALSE;
            if (!password) {
                  dialog = GNOME_PASSWORD_DIALOG (gnome_password_dialog_new ("",
                                                               prompt,
                                                               NULL,
                                                               NULL,
                                                               TRUE));
                  gnome_password_dialog_set_show_username (dialog, FALSE);
                  gnome_password_dialog_set_show_userpass_buttons (dialog, FALSE);
                  gnome_password_dialog_set_show_domain (dialog, FALSE);
                  gnome_password_dialog_set_show_remember (dialog, TRUE);
                  
                  if (!gnome_password_dialog_run_and_block (dialog)) {
                        rb_debug ("got error return from gnome_password_dialog_run_and_block");
                        got_error = TRUE;
                        reply = dbus_message_new_error (message,
                                                "com.redhat.PrinterManager.noPasswordGiven",
                                                "No password provided");
                        if (!reply) {
                              g_warning ("out of memory");
                              return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
                        }
                  } else {
                        reply = dbus_message_new_method_return (message);
                        if (!reply) {
                              g_warning ("out of memory");
                              return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
                        }
                        
                        password = gnome_password_dialog_get_password (dialog);
                  }
            } else {
                  reply = dbus_message_new_method_return (message);
                  if (!reply) {
                        g_warning ("out of memory");
                        return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
                  }
                  dialog = NULL;
            }
            
            if (!got_error && !dbus_message_append_args (reply, DBUS_TYPE_STRING,
                                     &password,
                                     DBUS_TYPE_INVALID)) {
                  g_warning ("couldn't append arguments to reply");
                  dbus_message_unref (reply);
                  g_free (password);
                  return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
            }

            if (!got_error && dialog != NULL) {
                  remember = gnome_password_dialog_get_remember (dialog);
                  if (remember == GNOME_PASSWORD_DIALOG_REMEMBER_SESSION) {
                        rb_debug ("saving password to session keyring");
                        save_password_for_printer(printer_name, "session", password);
                  } else if (remember == GNOME_PASSWORD_DIALOG_REMEMBER_FOREVER) {
                        rb_debug ("saving password to default keyring");
                        save_password_for_printer(printer_name, NULL, password);
                  } else {
                        rb_debug ("not saving password");
                  }
            }

            if (!got_error)
                  g_free (password);

            if (!dbus_connection_send (connection, reply, NULL)) {
                  g_warning ("couldn't send reply to promptPassword");
                  dbus_message_unref (reply);
                  return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
            }
            dbus_message_unref (reply);
            return DBUS_HANDLER_RESULT_HANDLED;
      }
      return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
}

static void leak (void *nothing) { }

static int
idle_check_system_dbus (ECTrayIcon *icon)
{
      DBusConnection *dbus;
      DBusError error;

      dbus_error_init (&error);
      dbus = dbus_bus_get (DBUS_BUS_SYSTEM, &error);
      if (dbus == NULL) {
            rb_debug ("couldn't connect to system bus: %s", error.message);
            dbus_error_free (&error);
            icon->priv->idle_check_system_dbus_id = g_timeout_add (5000,
                                                       (GSourceFunc) idle_check_system_dbus,
                                                       icon);
            return FALSE;
      }

      GDK_THREADS_ENTER ();

      rb_debug ("acquired system bus");
      dbus_bus_add_match (dbus,
                      "type='signal',"
                      "interface='com.redhat.PrinterSpooler'",
                      NULL);
      dbus_connection_add_filter (dbus, handle_system_dbus_message, icon, leak);
        if (icon->priv->driverprompt != NULL)
            g_object_unref (icon->priv->driverprompt);
      icon->priv->driverprompt = ec_driver_prompt_new (dbus);
      dbus_connection_setup_with_g_main (dbus, NULL);
      icon->priv->system_dbus = dbus;

      GDK_THREADS_LEAVE ();

      return FALSE;
}

static int
idle_check_session_dbus (ECTrayIcon *icon)
{
      DBusConnection *dbus = NULL;
      DBusError error;

      GDK_THREADS_ENTER ();

      dbus_error_init (&error);
      dbus = dbus_bus_get (DBUS_BUS_SESSION, &error);
      if (dbus == NULL) {
            g_warning ("couldn't connect to session bus: %s", error.message);
            goto lose;
      }

      rb_debug ("acquired session bus");

      if (dbus_bus_request_name (dbus, "com.redhat.PrinterManager",
                           0, &error) < 0) {
            g_warning ("couldn't acquire service com.redhat.PrinterManager");
            goto lose;
      }
      rb_debug ("acquired com.redhat.PrinterManager");

      dbus_bus_add_match (dbus,
                      "type='signal',"
                      "interface='com.redhat.PrinterSpooler'",
                      NULL);
      dbus_connection_add_filter (dbus, handle_session_dbus_message, icon,
                            leak);

      dbus_connection_setup_with_g_main (dbus, NULL);
      icon->priv->session_dbus = dbus;
      rb_debug ("acquired session bus");

      if (dbus_connection_register_object_path (dbus,
                                      EC_OBJECT_PATH,
                                      &ec_vtable,
                                      icon) == FALSE) {
            g_warning ("couldn't register eggcups object!");
      }

#ifdef WITH_SESSION_CUPSD
      {
            GConfClient *client = gconf_client_get_default ();
            GError *error = NULL;
            gboolean run = gconf_client_get_bool (client, EC_CONF_SESSION_CUPSD,
                                          &error);
            if (error != NULL) {
                  run = FALSE;
            }

            if (icon->priv->cups_manager)
                  g_object_unref (icon->priv->cups_manager);
            if (run)
                  icon->priv->cups_manager = ec_cups_manager_new (dbus);
            else
                  icon->priv->cups_manager = NULL;
      }
#endif

      GDK_THREADS_LEAVE ();
      return FALSE;
 lose:
      GDK_THREADS_LEAVE ();

      if (dbus)
            dbus_connection_unref (dbus);
      dbus_error_free (&error);
      icon->priv->idle_check_session_dbus_id = g_timeout_add (5000,
                                                (GSourceFunc) idle_check_session_dbus,
                                                icon);
      return FALSE;
}

static void
row_inserted_cb (GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, ECTrayIcon *icon)
{
      sync_state (icon);
      if (icon->priv->idle_blink_id == 0) {
            struct ECTrayIconBlinkContext *ctxt = g_new0 (struct ECTrayIconBlinkContext, 1);
            ctxt->icon = g_object_ref (icon);
            ctxt->count = BLINK_DURATION_SECONDS / BLINK_CHANGE_SECONDS;
            icon->priv->idle_blink_id = g_timeout_add_full (G_PRIORITY_DEFAULT_IDLE,
                                                BLINK_CHANGE_SECONDS * 1000,
                                                (GSourceFunc) idle_blink,
                                                ctxt,
                                                idle_blink_ctxt_destroy);
                                          
      }
}

static void
row_deleted_cb (GtkTreeModel *model, GtkTreePath *path, ECTrayIcon *icon)
{
      sync_state (icon);
}

static void
row_changed_cb (GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, ECTrayIcon *icon)
{
      sync_state (icon);
}

static gboolean
job_visible_func (GtkTreeModel *model, GtkTreeIter *iter, ECTrayIcon *icon)
{
      ECJobState state;

      gtk_tree_model_get (model, iter, EC_JOB_MODEL_COLUMN_STATE, &state, -1);

      return state != EC_JOB_STATE_FINAL;
}

static gboolean
hide_window (ECTrayIcon *icon)
{
      GDK_THREADS_ENTER ();

      rb_debug ("hiding window");

      gtk_widget_hide (GTK_WIDGET (icon));

      icon->priv->idle_hide_id = 0;

      GDK_THREADS_LEAVE ();

      return FALSE;
}

static void
idle_blink_ctxt_destroy (gpointer data)
{
      struct ECTrayIconBlinkContext *ctxt = data;
      ctxt->icon->priv->idle_blink_id = 0;
      g_object_unref (ctxt->icon);
      g_free (ctxt);
}

static gboolean
idle_blink (struct ECTrayIconBlinkContext *ctxt)
{
      GDK_THREADS_ENTER ();
      
      if (ctxt->count == 0) {
            gtk_image_set_from_pixbuf (GTK_IMAGE (ctxt->icon->priv->icon_image),
                                 ctxt->icon->priv->jobs_pixbuf);

            GDK_THREADS_LEAVE ();
            return FALSE;
      }

      ctxt->count--;
      if (gtk_image_get_pixbuf (GTK_IMAGE (ctxt->icon->priv->icon_image)) == ctxt->icon->priv->blank_pixbuf)
            gtk_image_set_from_pixbuf (GTK_IMAGE (ctxt->icon->priv->icon_image),
                                 ctxt->icon->priv->jobs_pixbuf);
      else
            gtk_image_set_from_pixbuf (GTK_IMAGE (ctxt->icon->priv->icon_image),
                                 ctxt->icon->priv->blank_pixbuf);

      GDK_THREADS_LEAVE ();
      return TRUE;
}

static gboolean
sync_state (ECTrayIcon *icon)
{
      guint count = gtk_tree_model_iter_n_children (icon->priv->filtered_model, NULL);
      char *text;

      rb_debug ("%u jobs outstanding", count);

      gtk_widget_set_sensitive (GTK_WIDGET (icon->priv->icon_image), count > 0);
      if (count > 0) {
            text = g_strdup_printf (_("%d documents queued"), count);         
            gtk_widget_show_all (GTK_WIDGET (icon));
            gtk_widget_show_all (GTK_WIDGET (icon->priv->ebox));
            if (icon->priv->idle_hide_id > 0) {
                  g_source_remove (icon->priv->idle_hide_id);
                  icon->priv->idle_hide_id = 0;
            }
      } else {
            text = g_strdup_printf (_("No documents queued for printing"));
      } 

      gtk_tooltips_set_tip (icon->priv->tips, GTK_WIDGET (icon), text, NULL);
      g_free (text);
      
      if (DISPLAY_TIMEOUT_SECONDS > 0
          && count == 0 && icon->priv->idle_hide_id == 0)
            icon->priv->idle_hide_id = g_timeout_add (DISPLAY_TIMEOUT_SECONDS * 1000,
                                            (GSourceFunc) hide_window,
                                            icon);
      return FALSE;
}

static void
popup_menu_position_cb (GtkMenu  *menu,
                  gint *x,
                  gint *y,
                  gboolean *push_in,
                  gpointer data)
{
      GtkWidget *w = GTK_WIDGET (data);
      GtkRequisition  requisition;
      gint wx, wy;

      g_return_if_fail (w != NULL);

      gtk_widget_size_request (GTK_WIDGET (menu), &requisition);

      gdk_window_get_origin (w->window, &wx, &wy);

      if (*x < wx)
            *x = wx;
      else if (*x > wx + w->allocation.width)
            *x = wx + w->allocation.width;

      if (*x + requisition.width > gdk_screen_width())
            *x = gdk_screen_width() - requisition.width;

      if (*y < wy)
            *y = wy;
       else if (*y > wy + w->allocation.height)
            *y = wy + w->allocation.height;

      if (*y + requisition.height > gdk_screen_height())
            *y = gdk_screen_height() - requisition.height;

      *push_in = TRUE;
}

static gboolean
icon_button_press_cb (GtkWidget *widget,
                  GdkEventButton *event,
                  ECTrayIcon *icon)
{
      if (event->button == 1) {
            if (icon->priv->list_window == NULL)
                  icon->priv->list_window = ec_job_list_new (icon->priv->model);
            gtk_window_present (GTK_WINDOW (icon->priv->list_window));
      }

      if (event->button == 3) {
            GtkWidget *menu;

            menu = gtk_item_factory_get_widget (icon->priv->popup_factory, "");
            gtk_menu_popup (GTK_MENU (menu),
                        NULL,
                        NULL,
                        popup_menu_position_cb,
                        icon,
                        event->button,
                        event->time);
      }

      return FALSE;
}

static void
icon_destroy_cb (GtkWidget *widget, gpointer user_data)
{
      gtk_main_quit ();
}

static char *
item_factory_trans_cb (const gchar *path,
                   gpointer data)
{
      return _((gchar *) path);
}

static void
job_state_changed_cb (ECJobModel   *model,
                  gchar        *printer,
                  gint          local_jobid,
                  GnomeCupsJob *job,
                  gpointer      user_data)
{
      GtkWidget *dialog;
      const char *secondary;

      switch (job->state)
            {
            case IPP_JOB_HELD:
                  if (ec_job_model_job_get_pending_state (model, local_jobid) == IPP_JOB_HELD) {
                        ec_job_model_job_set_pending_state (model, local_jobid, 0);
                        return;
                  }
                  
                  if (strstr (job->state_reason, "resources-are-not-ready"))
                        secondary = _("At least one of the resources needed "
                                    "by the document, such as media, fonts, "
                                    "resource objects, etc., is not ready.");
                  else if (strstr (job->state_reason, "printer-stopped-partly"))
                        secondary = _("The printer is partly stopped.");
                  else if (strstr (job->state_reason, "printer-stopped"))
                        secondary = _("The printer is stopped.");
                  else if (strstr (job->state_reason, "job-hold-until-specified"))
                        secondary = _("Someone else has paused it.");
                  else
                        secondary = _("You may want to find out why.");

                  dialog = gtk_message_dialog_new (NULL,
                                           0,
                                           GTK_MESSAGE_WARNING,
                                           GTK_BUTTONS_CLOSE,
                                           _("Printing of \"%s\" "
                                             "on printer \"%s\" was paused."),
                                           job->name,
                                           printer);
                  gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
                                                  "%s",
                                                  secondary);
                  gtk_dialog_run (GTK_DIALOG (dialog));
                  gtk_widget_destroy (dialog);
                  break;
            case IPP_JOB_CANCELLED:
                  if (ec_job_model_job_get_pending_state (model, local_jobid) == IPP_JOB_CANCELLED) {
                        ec_job_model_job_set_pending_state (model, local_jobid, 0);
                        return;
                  }

                  if (strstr (job->state_reason, "job-canceled-by-operator"))
                        secondary = _("An operator has cancelled it.");
                  else if (strstr (job->state_reason, "job-canceled-at-device"))
                        secondary = _("Someone at the printer has cancelled it.");
                  else
                        secondary = _("You may want to find out why.");

                  dialog = gtk_message_dialog_new (NULL,
                                           0,
                                           GTK_MESSAGE_WARNING,
                                           GTK_BUTTONS_CLOSE,
                                           _("Printing of \"%s\" "
                                             "on printer \"%s\" was cancelled."),
                                           job->name,
                                           printer);
                  gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
                                                  "%s",
                                                  secondary);
                  gtk_dialog_run (GTK_DIALOG (dialog));
                  gtk_widget_destroy (dialog);
                  break;
            case IPP_JOB_ABORTED:
                  if (strstr (job->state_reason, "unsupported-compression") ||
                      strstr (job->state_reason, "compression-error"))
                        secondary = _("The document was aborted by the system "
                                    "because the printer could not "
                                    "decompress the data.");

                  else if (strstr (job->state_reason, "unsupported-document-format") ||
                         strstr (job->state_reason, "document-format-error"))
                        secondary = _("The document was aborted by the system "
                                    "because the printer did not understand "
                                    "the document format.");
      else
                        secondary = _("You may want to find out why.");

                  dialog = gtk_message_dialog_new (NULL,
                                           0,
                                           GTK_MESSAGE_WARNING,
                                           GTK_BUTTONS_CLOSE,
                                           _("Printing of \"%s\" "
                                             "on printer \"%s\" was aborted."),
                                           job->name,
                                           printer);
                  gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
                                                  "%s",
                                                  secondary);
                  gtk_dialog_run (GTK_DIALOG (dialog));
                  gtk_widget_destroy (dialog);
                  break;
            default: ;
            }
}


Generated by  Doxygen 1.6.0   Back to index