Revision 5287 (by squisher, 2008/08/22 05:34:26) Fixing compilation without hal
/* $Id: xfburn-device-box.c 5287 2008-08-22 05:34:26Z squisher $ */
/*
 * Copyright (c) 2006 Jean-François Wauthy (pollux@xfce.org)
 *
 * 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 Library 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.
 */

#ifdef	HAVE_CONFIG_H
#include <config.h>
#endif /* !HAVE_CONFIG_H */

#include <gtk/gtk.h>
#include <libxfce4util/libxfce4util.h>
#include <libxfcegui4/libxfcegui4.h>

#ifdef HAVE_THUNAR_VFS
#include <thunar-vfs/thunar-vfs.h>
#endif

#include "xfburn-device-list.h"
#include "xfburn-device-box.h"
#include "xfburn-hal-manager.h"
#include "xfburn-settings.h"
#include "xfburn-utils.h"
#include "xfburn-blank-dialog.h"

#define XFBURN_DEVICE_BOX_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), XFBURN_TYPE_DEVICE_BOX, XfburnDeviceBoxPrivate))

enum {
  DEVICE_CHANGED,
  DISC_REFRESHED,
  LAST_SIGNAL,
};

enum {
  PROP_0,
  PROP_SHOW_WRITERS_ONLY,
  PROP_SHOW_SPEED_SELECTION,
  PROP_SHOW_MODE_SELECTION,
  PROP_DISC_STATUS,
  PROP_VALID,
  PROP_BLANK_MODE,
};

enum {
  DEVICE_NAME_COLUMN,
  DEVICE_POINTER_COLUMN,
  DEVICE_N_COLUMNS,
};

enum {
  SPEED_TEXT_COLUMN,
  SPEED_VALUE_COLUMN,
  SPEED_N_COLUMNS,
};

enum {
  MODE_TEXT_COLUMN,
  MODE_VALUE_COLUMN,
  MODE_N_COLUMNS,
};

/* private struct */
typedef struct
{
  gboolean show_writers_only;
  gboolean show_speed_selection;
  gboolean show_mode_selection;
  gboolean valid_disc;
  gboolean blank_mode;
  
  GtkWidget *combo_device;

  GtkWidget *hbox_refresh;
  GtkWidget *disc_label;
  
  GtkWidget *hbox_speed_selection;
  GtkWidget *combo_speed;

  GtkWidget *status_label;
  gchar *status_text;

  GtkWidget *hbox_mode_selection;
  GtkWidget *combo_mode;

  gboolean have_asked_for_blanking;
#ifdef HAVE_THUNAR_VFS
  ThunarVfsVolumeManager *thunar_volman;
#endif

#ifdef HAVE_HAL
  gulong volume_changed_handlerid;
#endif
} XfburnDeviceBoxPrivate;

/* prototypes */
static void xfburn_device_box_class_init (XfburnDeviceBoxClass *);
static void xfburn_device_box_init (XfburnDeviceBox *);
static void xfburn_device_box_finalize (GObject * object);
static void xfburn_device_box_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec);
static void xfburn_device_box_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec);

static guint ask_for_blanking (XfburnDeviceBoxPrivate *priv);
static void status_label_update (XfburnDeviceBoxPrivate *priv);
static void update_status_label_visibility ();
static XfburnDevice * get_selected_device (XfburnDeviceBoxPrivate *priv);
static void cb_speed_refresh_clicked (GtkButton *button, XfburnDeviceBox *box);
static gboolean check_disc_validity (XfburnDeviceBoxPrivate *priv);
static void cb_combo_device_changed (GtkComboBox *combo, XfburnDeviceBox *box);
#ifdef HAVE_HAL
static void cb_volumes_changed (XfburnHalManager *halman, XfburnDeviceBox *box);
#endif

/* globals */
static GtkVBoxClass *parent_class = NULL;

/*************************/
/* XfburnDeviceBox class */
/*************************/
static guint signals[LAST_SIGNAL];

GtkType
xfburn_device_box_get_type (void)
{
  static GtkType device_box_type = 0;

  if (!device_box_type)
    {
      static const GTypeInfo device_box_info = {
        sizeof (XfburnDeviceBoxClass),
        NULL,
        NULL,
        (GClassInitFunc) xfburn_device_box_class_init,
        NULL,
        NULL,
        sizeof (XfburnDeviceBox),
        0,
        (GInstanceInitFunc) xfburn_device_box_init
      };

      device_box_type = g_type_register_static (GTK_TYPE_VBOX, "XfburnDeviceBox", &device_box_info, 0);
    }

  return device_box_type;
}

static void
xfburn_device_box_class_init (XfburnDeviceBoxClass * klass)
{
  GObjectClass *object_class = G_OBJECT_CLASS (klass);
  
  g_type_class_add_private (klass, sizeof (XfburnDeviceBoxPrivate));

  parent_class = g_type_class_peek_parent (klass);
  
  object_class->finalize = xfburn_device_box_finalize;
  object_class->set_property = xfburn_device_box_set_property;
  object_class->get_property = xfburn_device_box_get_property;
  
  signals[DEVICE_CHANGED] = g_signal_new ("device-changed", XFBURN_TYPE_DEVICE_BOX, G_SIGNAL_ACTION,
                                          G_STRUCT_OFFSET (XfburnDeviceBoxClass, device_changed),
                                          NULL, NULL, g_cclosure_marshal_VOID__STRING,
                                          G_TYPE_NONE, 1, G_TYPE_STRING);
  signals[DISC_REFRESHED] = g_signal_new ("disc-refreshed", XFBURN_TYPE_DEVICE_BOX, G_SIGNAL_ACTION,
                                          G_STRUCT_OFFSET (XfburnDeviceBoxClass, disc_refreshed),
                                          NULL, NULL, g_cclosure_marshal_VOID__STRING,
                                          G_TYPE_NONE, 1, G_TYPE_STRING);
    
    
  g_object_class_install_property (object_class, PROP_SHOW_WRITERS_ONLY, 
                                   g_param_spec_boolean ("show-writers-only", _("Show writers only"),
                                                        _("Show writers only"), FALSE, G_PARAM_READWRITE));
  g_object_class_install_property (object_class, PROP_SHOW_SPEED_SELECTION, 
                                   g_param_spec_boolean ("show-speed-selection", _("Show speed selection"),
                                                        _("Show speed selection combo"), 
                                                        FALSE, G_PARAM_READWRITE));
  g_object_class_install_property (object_class, PROP_SHOW_MODE_SELECTION, 
                                   g_param_spec_boolean ("show-mode-selection", _("Show mode selection"),
                                                        _("Show mode selection combo"), 
                                                        FALSE, G_PARAM_READWRITE));
  g_object_class_install_property (object_class, PROP_DISC_STATUS,
                                   g_param_spec_int ("disc-status", _("Disc status"),
                                                      _("The status of the disc in the drive"), 
                                                      0, BURN_DISC_UNSUITABLE, 0, G_PARAM_READABLE));
  g_object_class_install_property (object_class, PROP_VALID, 
                                   g_param_spec_boolean ("valid", _("Is it a valid combination"),
                                                        _("Is the combination of hardware and disc valid to burn the composition?"), 
                                                        FALSE, G_PARAM_READABLE));
  g_object_class_install_property (object_class, PROP_BLANK_MODE, 
                                   g_param_spec_boolean ("blank-mode", _("Blank mode"),
                                                        _("The blank mode shows different disc status messages than regular mode"), 
                                                        FALSE, G_PARAM_READWRITE));
}

static void
xfburn_device_box_init (XfburnDeviceBox * box)
{
  XfburnDeviceBoxPrivate *priv = XFBURN_DEVICE_BOX_GET_PRIVATE (box);

  GtkWidget *label, *img, *button;
  GList *device = NULL;
  GtkListStore *store = NULL;
  GtkCellRenderer *cell;
  //GtkWidget *hbox;
  gboolean have_device;
  
  /* devices */
  store = gtk_list_store_new (DEVICE_N_COLUMNS, G_TYPE_STRING, G_TYPE_POINTER);
  priv->combo_device = gtk_combo_box_new_with_model (GTK_TREE_MODEL (store));
  g_object_unref (store);

  cell = gtk_cell_renderer_text_new ();
  gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (priv->combo_device), cell, TRUE);
  gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (priv->combo_device), cell, "text", DEVICE_NAME_COLUMN, NULL);
  gtk_widget_show (priv->combo_device);
  gtk_box_pack_start (GTK_BOX (box), priv->combo_device, FALSE, FALSE, BORDER);

  device = xfburn_device_list_get_list ();
  have_device = (device != NULL);

  while (device) {
    XfburnDevice *device_data = (XfburnDevice *) device->data;
    GtkTreeIter iter;

    gtk_list_store_append (store, &iter);
    gtk_list_store_set (store, &iter, DEVICE_NAME_COLUMN, device_data->name, DEVICE_POINTER_COLUMN, device_data, -1);

    device = g_list_next (device);
  }
  gtk_widget_set_sensitive (priv->combo_device, have_device);
  
  /*
  hbox = gtk_hbox_new (FALSE, 0);
  gtk_widget_show (hbox);
  gtk_box_pack_start (GTK_BOX (box), hbox, FALSE, FALSE, BORDER);
  */

  /* disc label */
  priv->hbox_refresh = gtk_hbox_new (FALSE, 0);
  gtk_widget_show (priv->hbox_refresh);
  gtk_box_pack_start (GTK_BOX (box), priv->hbox_refresh, TRUE, TRUE, BORDER);

  priv->disc_label = gtk_label_new ("");
  gtk_widget_show (priv->disc_label);
  gtk_box_pack_start (GTK_BOX (priv->hbox_refresh), priv->disc_label, TRUE, TRUE, BORDER);

  /* refresh */
  img = gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_SMALL_TOOLBAR);
  gtk_widget_show (img);
  button = gtk_button_new ();
  gtk_container_add (GTK_CONTAINER (button), img);
  gtk_widget_show (button);
  //gtk_box_pack_start (GTK_BOX (priv->hbox_speed_selection), button, FALSE, FALSE, 0);
  gtk_box_pack_start (GTK_BOX (priv->hbox_refresh), button, FALSE, FALSE, BORDER);
  gtk_widget_set_sensitive (button, have_device);

  g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (cb_speed_refresh_clicked), box);

  /* speed */
  priv->hbox_speed_selection = gtk_hbox_new (FALSE, 0);
  gtk_widget_show (priv->hbox_speed_selection);
  gtk_box_pack_start (GTK_BOX (box), priv->hbox_speed_selection, TRUE, TRUE, BORDER);

  label = gtk_label_new_with_mnemonic (_("_Speed:"));
  gtk_widget_show (label);
  gtk_box_pack_start (GTK_BOX (priv->hbox_speed_selection), label, FALSE, FALSE, BORDER);

  store = gtk_list_store_new (SPEED_N_COLUMNS, G_TYPE_STRING, G_TYPE_INT);
  priv->combo_speed = gtk_combo_box_new_with_model (GTK_TREE_MODEL (store));
  g_object_unref (store);

  cell = gtk_cell_renderer_text_new ();
  gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (priv->combo_speed), cell, TRUE);
  gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (priv->combo_speed), cell, "text", SPEED_TEXT_COLUMN, NULL);
  gtk_widget_show (priv->combo_speed);
  gtk_box_pack_start (GTK_BOX (priv->hbox_speed_selection), priv->combo_speed, TRUE, TRUE, BORDER);

  /* mode */
  priv->hbox_mode_selection = gtk_hbox_new (FALSE, 0);
  gtk_widget_show (priv->hbox_mode_selection);
  gtk_box_pack_start (GTK_BOX (box), priv->hbox_mode_selection, FALSE, FALSE, BORDER);

  label = gtk_label_new_with_mnemonic (_("Write _mode:"));
  gtk_widget_show (label);
  gtk_box_pack_start (GTK_BOX (priv->hbox_mode_selection), label, FALSE, FALSE, BORDER);

  store = gtk_list_store_new (MODE_N_COLUMNS, G_TYPE_STRING, G_TYPE_INT);
  priv->combo_mode = gtk_combo_box_new_with_model (GTK_TREE_MODEL (store));
  g_object_unref (store);

  cell = gtk_cell_renderer_text_new ();
  gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (priv->combo_mode), cell, TRUE);
  gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (priv->combo_mode), cell, "text", MODE_TEXT_COLUMN, NULL);
  gtk_widget_show (priv->combo_mode);
  gtk_box_pack_start (GTK_BOX (priv->hbox_mode_selection), priv->combo_mode, TRUE, TRUE, BORDER);
  gtk_widget_set_sensitive (priv->combo_mode, have_device);

  /* status label */
  priv->status_label = gtk_label_new ("");
  priv->status_text = "";
  gtk_widget_show (priv->status_label);
  gtk_box_pack_start (GTK_BOX (box), priv->status_label, FALSE, FALSE, 0);

  gtk_combo_box_set_active (GTK_COMBO_BOX (priv->combo_device), 0);
  g_signal_connect (G_OBJECT (priv->combo_device), "changed", G_CALLBACK (cb_combo_device_changed), box);

#ifdef HAVE_HAL
  priv->volume_changed_handlerid = g_signal_connect (G_OBJECT (xfburn_hal_manager_get_instance ()), "volume-changed", G_CALLBACK (cb_volumes_changed), box);
#endif
#ifdef HAVE_THUNAR_VFS
  /*
  priv->thunar_volman = thunar_vfs_volume_manager_get_default ();
  if (priv->thunar_volman != NULL) {
    //g_signal_connect (G_OBJECT (priv->thunar_volman), "volumes-added", G_CALLBACK (cb_volumes_changed), box);
    //g_signal_connect (G_OBJECT (priv->thunar_volman), "volumes-removed", G_CALLBACK (cb_volumes_changed), box);
  } else {
    g_warning ("Error trying to access the thunar-vfs-volume-manager!");
  }
  */
#endif

  priv->have_asked_for_blanking = FALSE;
}

static void
xfburn_device_box_finalize (GObject * object)
{
#ifdef HAVE_HAL
  XfburnDeviceBoxPrivate *priv = XFBURN_DEVICE_BOX_GET_PRIVATE (object);
#endif

#ifdef HAVE_THUNAR_VFS
  //g_object_unref (priv->thunar_volman);
#endif
#ifdef HAVE_HAL
  //g_object_unref (priv->hal_manager);
  g_signal_handler_disconnect (xfburn_hal_manager_get_instance (), priv->volume_changed_handlerid);
#endif

  G_OBJECT_CLASS (parent_class)->finalize (object);
}

static void
xfburn_device_box_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
{
  XfburnDeviceBoxPrivate *priv = XFBURN_DEVICE_BOX_GET_PRIVATE (object);

  switch (prop_id) {
    case PROP_SHOW_WRITERS_ONLY:
      g_value_set_boolean (value, priv->show_writers_only);
      break;
    case PROP_SHOW_SPEED_SELECTION:
      g_value_set_boolean (value, priv->show_speed_selection);
      break;
    case PROP_SHOW_MODE_SELECTION:
      g_value_set_boolean (value, priv->show_mode_selection);
      break;
    case PROP_DISC_STATUS:
      g_value_set_int (value, xfburn_device_list_get_disc_status());
      break;
    case PROP_VALID:
      g_value_set_boolean (value, priv->valid_disc);
      break;
    case PROP_BLANK_MODE:
      g_value_set_boolean (value, priv->blank_mode);
      break;
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
  }
}

static void
xfburn_device_box_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
{
  XfburnDeviceBoxPrivate *priv = XFBURN_DEVICE_BOX_GET_PRIVATE (object);
  
  switch (prop_id) {
    case PROP_SHOW_WRITERS_ONLY:
      priv->show_writers_only = g_value_get_boolean (value);
      break;
    case PROP_SHOW_SPEED_SELECTION:
      priv->show_speed_selection = g_value_get_boolean (value);
      if (priv->show_speed_selection)
        gtk_widget_show (priv->hbox_speed_selection);
      else
        gtk_widget_hide (priv->hbox_speed_selection);
      update_status_label_visibility (priv);
      break;
    case PROP_SHOW_MODE_SELECTION:
      priv->show_mode_selection = g_value_get_boolean (value);
      if (priv->show_mode_selection)
        gtk_widget_show (priv->hbox_mode_selection);
      else
        gtk_widget_hide (priv->hbox_mode_selection);
      update_status_label_visibility (priv);
      break;
    case PROP_BLANK_MODE:
      priv->blank_mode = g_value_get_boolean (value);
      break;
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
  }
}

/*************/
/* internals */
/*************/

static void
update_status_label_visibility (XfburnDeviceBoxPrivate *priv)
{
  /*
  if (priv->show_mode_selection || priv->show_speed_selection)
    gtk_widget_show (priv->status_label);
  else
    gtk_widget_hide (priv->status_label);
  */

}

static void
empty_speed_list_dialog ()
{
  GtkDialog *dialog;
  GtkWidget *label;
  GtkWidget *check_show_notice;

  if (!xfburn_settings_get_boolean ("show-empty-speed-list-notice", TRUE))
    return;

  dialog = (GtkDialog *) gtk_dialog_new_with_buttons (_("Empty speed list"),
                                  NULL,
                                  GTK_DIALOG_DESTROY_WITH_PARENT,
                                  GTK_STOCK_CLOSE,
                                  GTK_RESPONSE_CLOSE,
                                  NULL);

  label = gtk_label_new (NULL);
  gtk_label_set_markup (GTK_LABEL (label), _("<b>Unable to retrieve the speed list for the drive.</b>\n\nThis is a known bug, which occurs with some drives. Please report it to <i>xfburn@xfce.org</i> together with the console output to increase the chances that it will get fixed.\n\nBurning should still work, but if there are problems anyways, please let us know.\n\n<i>Thank you!</i>")
                        );
  gtk_label_set_selectable (GTK_LABEL (label), TRUE);
  gtk_label_set_width_chars (GTK_LABEL (label), 30);
  gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
  gtk_box_pack_start (GTK_BOX (dialog->vbox), label, TRUE, TRUE, BORDER);
  gtk_widget_show (label);


  check_show_notice = gtk_check_button_new_with_mnemonic (_("Continue to _show this notice"));
  gtk_box_pack_end (GTK_BOX (dialog->vbox), check_show_notice, TRUE, TRUE, BORDER);
  gtk_widget_show (check_show_notice);
  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (check_show_notice), TRUE);


  switch (gtk_dialog_run (GTK_DIALOG (dialog))) {
    case GTK_RESPONSE_CLOSE:
      xfburn_settings_set_boolean ("show-empty-speed-list-notice", 
                                   gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (check_show_notice)));
      break;
    default:
      /* do nothing */
      break;
  }
  gtk_widget_destroy (GTK_WIDGET (dialog));
}

static void
fill_combo_speed (XfburnDeviceBox *box, XfburnDevice *device)
{
  XfburnDeviceBoxPrivate *priv = XFBURN_DEVICE_BOX_GET_PRIVATE (box);
  GtkTreeModel *model = gtk_combo_box_get_model (GTK_COMBO_BOX (priv->combo_speed));
  GSList *el = device->supported_cdr_speeds;
  int profile_no = xfburn_device_list_get_profile_no ();
  int factor;
  GtkTreeIter iter_max;

  gtk_list_store_clear (GTK_LIST_STORE (model));

  if (el == NULL) {
    /* a valid disc is in the drive, but no speed list is present */
    GtkTreeIter iter;
    gchar *str;

    empty_speed_list_dialog ();

    str = _("default");
    gtk_list_store_append (GTK_LIST_STORE (model), &iter);
    gtk_list_store_set (GTK_LIST_STORE (model), &iter, SPEED_TEXT_COLUMN, str, SPEED_VALUE_COLUMN, -1, -1);
  }

  /* check profile, so we can convert from 'kb/s' into 'x' rating */
  if (profile_no != 0) {
    /* this will fail if newer disk types get supported */
    if (profile_no <= 0x0a)
      factor = CDR_1X_SPEED;
    else
      /* assume DVD for now */
      factor = DVD_1X_SPEED;
  } else {
    factor = 1;
  }

  while (el) {
    gint write_speed = GPOINTER_TO_INT (el->data);
    GtkTreeIter iter;
    gchar *str = NULL;
    gint speed;

    speed = write_speed / factor;
    str = g_strdup_printf ("%d", speed);
    //DBG ("added speed: %d kb/s => %d x", el->write_speed, speed);

    gtk_list_store_append (GTK_LIST_STORE (model), &iter);
    gtk_list_store_set (GTK_LIST_STORE (model), &iter, SPEED_TEXT_COLUMN, str, SPEED_VALUE_COLUMN, write_speed, -1);
    g_free (str);

    el = g_slist_next (el);
  }

  gtk_list_store_append (GTK_LIST_STORE (model), &iter_max);

  gtk_list_store_set (GTK_LIST_STORE (model), &iter_max, SPEED_TEXT_COLUMN, _("Max"), SPEED_VALUE_COLUMN, 0, -1);
  gtk_combo_box_set_active (GTK_COMBO_BOX (priv->combo_speed), gtk_tree_model_iter_n_children (model, NULL) - 1);
}

static void
status_label_update (XfburnDeviceBoxPrivate *priv)
{
  gchar * text;
  gboolean sensitive;

  sensitive = GTK_WIDGET_SENSITIVE (priv->combo_device);

  //DBG ("sensitive = %d", sensitive);

  if (sensitive)
    text = g_strdup_printf ("<span weight=\"bold\" foreground=\"darkred\" stretch=\"semiexpanded\">%s</span>", priv->status_text);
  else
    text = g_strdup_printf ("<span weight=\"bold\" foreground=\"gray\" stretch=\"semiexpanded\">%s</span>", priv->status_text);

  gtk_label_set_markup (GTK_LABEL(priv->status_label), text);
  g_free (text);
}

static guint
ask_for_blanking (XfburnDeviceBoxPrivate *priv)
{
  gboolean do_blank;

  if (priv->have_asked_for_blanking)
    return FALSE;

  gdk_threads_enter ();
  priv->have_asked_for_blanking = TRUE;
  do_blank = xfburn_ask_yes_no (GTK_MESSAGE_QUESTION, "A full, but erasable disc is in the drive",
                                         "Do you want to blank the disc, so that it can be used for the upcoming burn process?");

  if (do_blank) {
    GtkDialog *blank_dialog = GTK_DIALOG (xfburn_blank_dialog_new_eject (FALSE));
    gtk_dialog_run (blank_dialog);
    gtk_widget_destroy (GTK_WIDGET (blank_dialog));
  }
  gdk_threads_leave ();
  DBG ("done asking to blank");
  return FALSE;
}

static gboolean
check_disc_validity (XfburnDeviceBoxPrivate *priv)
{
  enum burn_disc_status disc_status = xfburn_device_list_get_disc_status ();
  int profile_no = xfburn_device_list_get_profile_no ();
  gboolean is_erasable = xfburn_device_list_disc_is_erasable ();
  XfburnDevice *device = get_selected_device (priv);
  
  gtk_label_set_text (GTK_LABEL (priv->disc_label), xfburn_device_list_get_profile_name ());

  if (!priv->blank_mode) {
    /* for burning */
    switch (profile_no) {
      case XFBURN_PROFILE_NONE:
        /* empty drive is caught later,
         * not sure if there would be another reason for 0x0 */
        priv->valid_disc = TRUE;
        break;
      case XFBURN_PROFILE_CDR:
        priv->valid_disc = device->cdr;
        break;
      case XFBURN_PROFILE_CDRW:
        priv->valid_disc = device->cdrw;
        break;
      case XFBURN_PROFILE_DVDRAM:
        priv->valid_disc = device->dvdram;
        break;
      case XFBURN_PROFILE_DVD_MINUS_R:
      case XFBURN_PROFILE_DVD_MINUS_RW_OVERWRITE:
      case XFBURN_PROFILE_DVD_MINUS_RW_SEQUENTIAL:
      case XFBURN_PROFILE_DVD_MINUS_R_DL:
      case XFBURN_PROFILE_DVD_PLUS_R:
      case XFBURN_PROFILE_DVD_PLUS_R_DL:
      case XFBURN_PROFILE_DVD_PLUS_RW:
        priv->valid_disc = device->dvdr;
        break;
      default:
        g_warning ("Unknown disc profile 0x%x!", profile_no);
        priv->valid_disc = TRUE;
        break;
    }

    if (!priv->valid_disc) {
        priv->status_text = _("Drive can't burn on the inserted disc!");
    } else {
      priv->valid_disc = (disc_status == BURN_DISC_BLANK); 
      /* Not sure if we support appending yet, so let's disable it for the time being
       * || (disc_status == BURN_DISC_APPENDABLE); */

      if (!priv->valid_disc) {
        switch (disc_status) {
          case BURN_DISC_EMPTY:
            priv->status_text = _("Drive is empty!");
            break;
          case BURN_DISC_FULL:
            priv->status_text = _("Inserted disc is full!");
            break;
          case BURN_DISC_UNSUITABLE:
            priv->status_text = _("Inserted disc is unsuitable!");
            break;
          case BURN_DISC_UNGRABBED:
            priv->status_text = _("No access to drive (mounted?)");
            break;
          default:
            /* if there is no detected device, then don't print an error message as it is expected to not have a disc status */
            if (device != NULL) {
              priv->status_text = _("Error determining disc!");
              DBG ("weird disc_status = %d", disc_status);
            }
        }
      }
    }

    if (!priv->valid_disc && disc_status == BURN_DISC_FULL && is_erasable) {
      g_idle_add ((GSourceFunc) ask_for_blanking, priv);
    }
  }
  else { /* priv->blank_mode == TRUE */
    priv->valid_disc = is_erasable && (disc_status != BURN_DISC_BLANK);

    if (!priv->valid_disc) {
      switch (profile_no) {
        case XFBURN_PROFILE_CDR:
        case XFBURN_PROFILE_DVD_MINUS_R:
        case XFBURN_PROFILE_DVD_MINUS_R_DL:
        case XFBURN_PROFILE_DVD_PLUS_R:
        case XFBURN_PROFILE_DVD_PLUS_R_DL:
          priv->status_text = _("Write-once disc, no blanking possible");
          break;
        case XFBURN_PROFILE_DVD_PLUS_RW:
          priv->status_text = _("DVD+RW does not need blanking");
          break;
        default:
          switch (disc_status) {
            case BURN_DISC_EMPTY:
              priv->status_text = _("Drive is empty!");
              break;
            case BURN_DISC_BLANK:
              priv->status_text = _("Inserted disc is already blank!");
              break;
            case BURN_DISC_UNSUITABLE:
              priv->status_text = _("Inserted disc is unsuitable!");
              break;
            case BURN_DISC_UNGRABBED:
              priv->status_text = _("No access to drive (mounted?)");
              break;
            default:
              priv->status_text = _("Error determining disc!");
              DBG ("weird disc_status = %d", disc_status);
          }
      }
    }
  }

  gtk_widget_set_sensitive (priv->combo_speed, priv->valid_disc);

  if (priv->valid_disc)
    priv->status_text = "";

  status_label_update (priv);
  return priv->valid_disc;
}

static void
fill_combo_mode (XfburnDeviceBox *box, XfburnDevice *device)
{
  XfburnDeviceBoxPrivate *priv = XFBURN_DEVICE_BOX_GET_PRIVATE (box);
  GtkTreeModel *model = gtk_combo_box_get_model (GTK_COMBO_BOX (priv->combo_mode));
  GtkTreeIter iter;

  gtk_list_store_clear (GTK_LIST_STORE (model));

  if (device->tao_block_types) {
    gtk_list_store_append (GTK_LIST_STORE (model), &iter);
    gtk_list_store_set (GTK_LIST_STORE (model), &iter, MODE_TEXT_COLUMN, "TAO", MODE_VALUE_COLUMN, WRITE_MODE_TAO, -1);
  }
  if (device->sao_block_types & BURN_BLOCK_SAO) {
    gtk_list_store_append (GTK_LIST_STORE (model), &iter);
    gtk_list_store_set (GTK_LIST_STORE (model), &iter, MODE_TEXT_COLUMN, "SAO", MODE_VALUE_COLUMN, WRITE_MODE_SAO, -1);
  }
  /*
   * RAW modes are not supported by libburn yet
   *
  if (device->raw_block_types & BURN_BLOCK_RAW16) {
    gtk_list_store_append (GTK_LIST_STORE (model), &iter);
    gtk_list_store_set (GTK_LIST_STORE (model), &iter, MODE_TEXT_COLUMN, "RAW16", MODE_VALUE_COLUMN, WRITE_MODE_RAW16, -1);
  }
  if (device->raw_block_types & BURN_BLOCK_RAW96P) {
    gtk_list_store_append (GTK_LIST_STORE (model), &iter);
    gtk_list_store_set (GTK_LIST_STORE (model), &iter, MODE_TEXT_COLUMN, "RAW96P", MODE_VALUE_COLUMN, WRITE_MODE_RAW96P, -1);
  }
  if (device->raw_block_types & BURN_BLOCK_RAW96R) {
    gtk_list_store_append (GTK_LIST_STORE (model), &iter);
    gtk_list_store_set (GTK_LIST_STORE (model), &iter, MODE_TEXT_COLUMN, "RAW96R", MODE_VALUE_COLUMN, WRITE_MODE_RAW96R, -1);
  }
  if (device->packet_block_types) {
    gtk_list_store_append (GTK_LIST_STORE (model), &iter);
    gtk_list_store_set (GTK_LIST_STORE (model), &iter, MODE_TEXT_COLUMN, "packet", MODE_VALUE_COLUMN, WRITE_MODE_PACKET, -1);
  }
  */
  
  gtk_combo_box_set_active (GTK_COMBO_BOX (priv->combo_mode), 0);
}

static XfburnDevice *
get_selected_device (XfburnDeviceBoxPrivate *priv)
{

  GtkTreeModel *model;
  GtkTreeIter iter;
  XfburnDevice * device = NULL;
  gboolean ret;

  model = gtk_combo_box_get_model (GTK_COMBO_BOX (priv->combo_device));
  ret = gtk_combo_box_get_active_iter (GTK_COMBO_BOX (priv->combo_device), &iter);
  if (ret)
    gtk_tree_model_get (model, &iter, DEVICE_POINTER_COLUMN, &device, -1);

  return device;
}

static XfburnDevice *
refresh_drive_info (XfburnDeviceBox *box)
{
  XfburnDeviceBoxPrivate *priv = XFBURN_DEVICE_BOX_GET_PRIVATE (box);
  XfburnDevice *device = NULL;
  
  device = xfburn_device_box_get_selected_device (box);
  if (G_UNLIKELY (device == NULL))
    return NULL;

  if (!xfburn_device_refresh_info (device, priv->show_speed_selection))
    return NULL;

  if (priv->show_speed_selection)
    fill_combo_speed (box, device);

  if (priv->show_mode_selection)
    fill_combo_mode (box,device);

  if (!check_disc_validity (priv))
    return NULL;

  return device;
}

static void
cb_speed_refresh_clicked (GtkButton *button, XfburnDeviceBox *box)
{
  XfburnDeviceBoxPrivate *priv = XFBURN_DEVICE_BOX_GET_PRIVATE (box);
  XfburnDevice *device;
  
  xfburn_busy_cursor (priv->combo_device);

  device = refresh_drive_info (box);

  xfburn_default_cursor (priv->combo_device);

  if (device == NULL)
    return;

  g_signal_emit (G_OBJECT (box), signals[DISC_REFRESHED], 0, device);
}

static void
cb_combo_device_changed (GtkComboBox *combo, XfburnDeviceBox *box)
{
  //XfburnDeviceBoxPrivate *priv = XFBURN_DEVICE_BOX_GET_PRIVATE (box);
  XfburnDevice *device;
  
  if (GTK_WIDGET_REALIZED (box))
    xfburn_busy_cursor (GTK_WIDGET (box));

  device = refresh_drive_info (box);

  if (GTK_WIDGET_REALIZED (box))
    xfburn_default_cursor (GTK_WIDGET (box));

  if (device == NULL)
    return;

  g_signal_emit (G_OBJECT (box), signals[DEVICE_CHANGED], 0, device);
}

#ifdef HAVE_HAL
static void
cb_volumes_changed (XfburnHalManager *halman, XfburnDeviceBox *box)
{
  gboolean visible;

  visible = GTK_WIDGET_VISIBLE (GTK_WIDGET(box));
  //DBG ("device box visibility: %d", visible);
  if (visible) {
    usleep (1000001);
    cb_speed_refresh_clicked (NULL, box);
  }
}
#endif

#ifdef HAVE_THUNAR_VFS
//(ThunarVfsVolumeManager *volman, gpointer volumes, XfburnDeviceBox *box)
#endif

static void
refresh (GtkWidget *widget)
{
  XfburnDeviceBox *box = XFBURN_DEVICE_BOX (widget);
  XfburnDeviceBoxPrivate *priv = XFBURN_DEVICE_BOX_GET_PRIVATE (box);

  cb_combo_device_changed (GTK_COMBO_BOX (priv->combo_device), box);
}

/******************/
/* public methods */
/******************/
GtkWidget *
xfburn_device_box_new (XfburnDeviceBoxFlags flags)
{
  GtkWidget *obj;

  obj = g_object_new (xfburn_device_box_get_type (), 
		      "show-writers-only", ((flags & SHOW_CD_WRITERS) != 0), 
                      "show-speed-selection", ((flags & SHOW_SPEED_SELECTION) != 0), 
		      "show-mode-selection", ((flags & SHOW_MODE_SELECTION) != 0),
		      "blank-mode", ((flags & BLANK_MODE) != 0),
                      NULL);
  
  refresh (obj);

  return obj;
}

gchar *
xfburn_device_box_get_selected (XfburnDeviceBox *box)
{
  XfburnDeviceBoxPrivate *priv = XFBURN_DEVICE_BOX_GET_PRIVATE (box);
  GtkTreeModel *model;
  GtkTreeIter iter;
  gchar *name = NULL;
  gboolean ret;

  model = gtk_combo_box_get_model (GTK_COMBO_BOX (priv->combo_device));
  ret = gtk_combo_box_get_active_iter (GTK_COMBO_BOX (priv->combo_device), &iter);
  if (ret)
    gtk_tree_model_get (model, &iter, DEVICE_NAME_COLUMN, &name, -1);

  return name;
}

XfburnDevice *
xfburn_device_box_get_selected_device (XfburnDeviceBox *box)
{
  XfburnDeviceBoxPrivate *priv = XFBURN_DEVICE_BOX_GET_PRIVATE (box);

  return get_selected_device (priv);
}

gint
xfburn_device_box_get_speed (XfburnDeviceBox *box)
{
  XfburnDeviceBoxPrivate *priv = XFBURN_DEVICE_BOX_GET_PRIVATE (box);
  
  GtkTreeModel *model;
  GtkTreeIter iter;
  gint speed = -1;
  gboolean ret;

  g_return_val_if_fail (priv->show_speed_selection, -1);

  model = gtk_combo_box_get_model (GTK_COMBO_BOX (priv->combo_speed));
  ret = gtk_combo_box_get_active_iter (GTK_COMBO_BOX (priv->combo_speed), &iter);
  if (ret)
    gtk_tree_model_get (model, &iter, SPEED_VALUE_COLUMN, &speed, -1);

  return speed;
}

void xfburn_device_box_set_sensitive (XfburnDeviceBox *box, gboolean sensitivity)
{
  XfburnDeviceBoxPrivate *priv = XFBURN_DEVICE_BOX_GET_PRIVATE (box);

  /* why do we need to explicitly set this? It gets grayed out even
   * without this call! */
  gtk_widget_set_sensitive (priv->combo_device, sensitivity);
  //DBG ("sensitive = %d", GTK_WIDGET_SENSITIVE (GTK_WIDGET (box)));
  status_label_update (priv);
}

XfburnWriteMode
xfburn_device_box_get_mode (XfburnDeviceBox *box)
{
  XfburnDeviceBoxPrivate *priv = XFBURN_DEVICE_BOX_GET_PRIVATE (box);
  
  GtkTreeModel *model;
  GtkTreeIter iter;
  gint mode = -1;
  gboolean ret;

  g_return_val_if_fail (priv->show_mode_selection, -1);

  model = gtk_combo_box_get_model (GTK_COMBO_BOX (priv->combo_mode));
  ret = gtk_combo_box_get_active_iter (GTK_COMBO_BOX (priv->combo_mode), &iter);
  if (ret)
    gtk_tree_model_get (model, &iter, SPEED_VALUE_COLUMN, &mode, -1);

  return mode;
}