/*
 * This file is part of GNAC - Gnome Audio Converter
 *
 * Copyright (C) 2007 - 2012 Gnac
 *    
 *    - DUPASQUIER  Benoit    <bdupasqu@src.gnome.org>
 *    - JOAQUIM     David     <djoaquim@src.gnome.org>
 *    - ROUX        Alexandre <alexroux@src.gnome.org>
 *
 * GNAC 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 3 of the License, or
 * (at your option) any later version.
 *
 * GNAC 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 GNAC; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, 
 * Boston, MA  02110-1301  USA
 */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include <glib/gi18n.h>
#include <glib/gprintf.h>
#include <gst/gst.h>

#ifdef HAVE_LIBNOTIFY
#include <libnotify/notify.h>
#endif

#ifdef HAVE_LIBUNIQUE
#include <unique/unique.h>
#endif

#include "gnac-bars.h"
#include "gnac-file-list.h"
#include "gnac-main.h"
#include "gnac-options.h"
#include "gnac-prefs.h"
#include "gnac-properties.h"
#include "gnac-settings.h"
#include "gnac-stock-items.h"
#include "gnac-ui.h"
#include "gnac-ui-utils.h"
#include "gnac-utils.h"
#include "profiles/gnac-profiles.h"
#include "profiles/gnac-profiles-manager.h"


static GSList        *filters;
static GtkBuilder    *gnac_main_builder = NULL;
static GtkFileFilter *default_file_filter;
static GtkStatusIcon *trayicon = NULL;
static GtkWidget     *gnac_file_chooser;
static gchar         *last_used_folder = NULL;
static gchar         *status_msg = NULL;
static gchar         *tooltip_path = NULL;
static guint          timeout_id;

static gint root_x;
static gint root_y;

static GtkTargetEntry target_list[] =  {
  { "text/uri-list", 0, TARGET_STRING }
};

static guint n_targets = G_N_ELEMENTS(target_list);

#ifdef HAVE_LIBUNIQUE
static UniqueApp *app;

enum {
  UNIQUE_CMD_0, /* unused: 0 is an invalid command */
  UNIQUE_CMD_ADD,
  UNIQUE_CMD_DEBUG,
  UNIQUE_CMD_VERBOSE
};
#endif /* HAVE_LIBUNIQUE */


static void
gnac_ui_update_trayicon_popup(void);


static void
gnac_ui_file_chooser_unref_filters(void)
{
  GSList *f;
  for (f = filters; f; f = g_slist_next(f)) {
    g_object_unref(f->data);
  }
  g_slist_free(f);
}


static void
gnac_ui_file_chooser_dispose(void)
{
  g_free(last_used_folder);

  if (GTK_IS_WIDGET(gnac_file_chooser)) {
    gnac_ui_file_chooser_unref_filters();
    gtk_widget_destroy(gnac_file_chooser);
  }
}


static void
gnac_ui_file_chooser_cell_data_func(GtkCellLayout   *layout,
                                    GtkCellRenderer *cell,
                                    GtkTreeModel    *model,
                                    GtkTreeIter     *iter,
                                    gpointer         data)
{
  GtkFileFilter *filter;
  gtk_tree_model_get(model, iter, 0, &filter, -1);
  g_object_set(cell, "text", gtk_file_filter_get_name(filter), NULL);
}


static GtkTreeModel *
gnac_ui_file_chooser_get_filters_model(void)
{
  guint          i;
  GSList        *f;
  GtkFileFilter *filter;
  GtkTreeIter    iter1, iter2, iter3;
  GtkTreeStore  *store;

  /* To translators: translation of filters' name can be
   * found in /usr/share/mime */
  const gchar *lossy_mime[][2] = {
    { "audio/mpeg"            , _("MP3 audio")           },
    { "audio/mp4"             , _("MPEG-4 audio")        },
    { "audio/x-musepack"      , _("Musepack audio")      },
    { "audio/ogg"             , _("Ogg Audio")           },
    { "audio/vnd.rn-realaudio", _("RealAudio document")  },
    { "audio/x-speex"         , _("Speex audio")         },
    { "audio/x-ms-wma"        , _("Windows Media audio") },
    { NULL, NULL }
  };

  const gchar *lossless_mime[][2] = {
    { "audio/x-ape"    , _("Monkey's audio") },
    { "audio/x-flac"   , _("Flac audio")     },
    { "audio/x-wav"    , _("WAV audio")      },
    { "audio/x-wavpack", _("WavPack audio")  },
    { NULL, NULL }
  };

  const gchar *playlists_mime[][2] = {
    { "audio/x-ms-asx"      , _("Microsoft ASX playlist") },
    { "audio/x-mpegurl"     , _("MP3 audio (streamed)")   },
    { "audio/x-scpls"       , _("MP3 ShoutCast playlist") },
    { "application/xspf+xml", _("XSPF playlist")          },
    { NULL, NULL }
  };

  const gchar *video_mime[][2] = {
    { "video/3gpp"                   , _("3GPP multimedia file") },
    { "video/x-ms-asf"               , _("ASF video")            },
    { "video/x-msvideo"              , _("AVI video")            },
    { "video/x-flv"                  , _("Flash video")          },
    { "video/x-matroska"             , _("Matroska video")       },
    { "video/mpeg"                   , _("MPEG video")           },
    { "video/mp4"                    , _("MPEG-4 video")         },
    { "application/ogg"              , _("Ogg multimedia file")  },
    { "video/ogg"                    , _("Ogg video")            },
    { "video/quicktime"              , _("QuickTime video")      },
    { "application/vnd.rn-realmedia" , _("RealMedia document")   },
    { "application/x-shockwave-flash", _("Shockwave Flash file") },
    { "video/x-ms-wmv"               , _("Windows Media video")  },
    { NULL, NULL }
  };

  filters = NULL;
  store = gtk_tree_store_new(1, G_TYPE_POINTER);

  /* All files */
  filter = gtk_file_filter_new();
  gtk_file_filter_add_pattern(filter, "*");
  gtk_file_filter_set_name(filter, _("All files"));
  gtk_tree_store_append(store, &iter1, NULL);
  gtk_tree_store_set(store, &iter1, 0, filter, -1);
  filters = g_slist_prepend(filters, filter);

  /* Supported files */
  filter = gtk_file_filter_new();
  gtk_file_filter_set_name(filter, _("Supported files"));
  i = 0;
  while (lossless_mime[i][0]) {
    gtk_file_filter_add_mime_type(filter, lossless_mime[i][0]);
    i++;
  }
  i = 0;
  while (lossy_mime[i][0]) {
    gtk_file_filter_add_mime_type(filter, lossy_mime[i][0]);
    i++;
  }
  i = 0;
  while (playlists_mime[i][0]) {
    gtk_file_filter_add_mime_type(filter, playlists_mime[i][0]);
    i++;
  }
  i = 0;
  while (video_mime[i][0]) {
    gtk_file_filter_add_mime_type(filter, video_mime[i][0]);
    i++;
  }
  gtk_tree_store_append(store, &iter1, NULL);
  gtk_tree_store_set(store, &iter1, 0, filter, -1);
  filters = g_slist_prepend(filters, filter);
  default_file_filter = filter;

  /* Audio files */
  filter = gtk_file_filter_new();
  gtk_file_filter_set_name(filter, _("Audio files"));
  i = 0;
  while (lossless_mime[i][0]) {
    gtk_file_filter_add_mime_type(filter, lossless_mime[i][0]);
    i++;
  }
  i = 0;
  while (lossy_mime[i][0]) {
    gtk_file_filter_add_mime_type(filter, lossy_mime[i][0]);
    i++;
  }
  i = 0;
  while (playlists_mime[i][0]) {
    gtk_file_filter_add_mime_type(filter, playlists_mime[i][0]);
    i++;
  }
  gtk_tree_store_append(store, &iter1, NULL);
  gtk_tree_store_set(store, &iter1, 0, filter, -1);
  filters = g_slist_prepend(filters, filter);

  /* Lossy files */
  filter = gtk_file_filter_new();
  gtk_file_filter_set_name(filter, _("Lossy files"));
  i = 0;
  while (lossy_mime[i][0]) {
    gtk_file_filter_add_mime_type(filter, lossy_mime[i][0]);
    i++;
  }
  gtk_tree_store_append(store, &iter2, &iter1);
  gtk_tree_store_set(store, &iter2, 0, filter, -1);
  filters = g_slist_prepend(filters, filter);

  /* Individual lossy files */
  i = 0;
  while (lossy_mime[i][0]) {
    filter = gtk_file_filter_new();
    gtk_file_filter_add_mime_type(filter, lossy_mime[i][0]);
    gtk_file_filter_set_name(filter, lossy_mime[i][1]);
    gtk_tree_store_append(store, &iter3, &iter2);
    gtk_tree_store_set(store, &iter3, 0, filter, -1);
    filters = g_slist_prepend(filters, filter);
    i++;
  }

  /* Lossless files */
  filter = gtk_file_filter_new();
  gtk_file_filter_set_name(filter, _("Lossless files"));
  i = 0;
  while (lossless_mime[i][0]) {
    gtk_file_filter_add_mime_type(filter, lossless_mime[i][0]);
    i++;
  }
  gtk_tree_store_append(store, &iter2, &iter1);
  gtk_tree_store_set(store, &iter2, 0, filter, -1);
  filters = g_slist_prepend(filters, filter);

  /* Individual lossless files */
  i = 0;
  while (lossless_mime[i][0]) {
    filter = gtk_file_filter_new();
    gtk_file_filter_add_mime_type(filter, lossless_mime[i][0]);
    gtk_file_filter_set_name(filter, lossless_mime[i][1]);
    gtk_tree_store_append(store, &iter3, &iter2);
    gtk_tree_store_set(store, &iter3, 0, filter, -1);
    filters = g_slist_prepend(filters, filter);
    i++;
  }

  /* Playlists */
  filter = gtk_file_filter_new();
  gtk_file_filter_set_name(filter, _("Playlists"));
  i = 0;
  while (playlists_mime[i][0]) {
    gtk_file_filter_add_mime_type(filter, playlists_mime[i][0]);
    i++;
  }
  gtk_tree_store_append(store, &iter1, NULL);
  gtk_tree_store_set(store, &iter1, 0, filter, -1);
  filters = g_slist_prepend(filters, filter);

  /* Individual playlists */
  i = 0;
  while (playlists_mime[i][0]) {
    filter = gtk_file_filter_new();
    gtk_file_filter_add_mime_type(filter, playlists_mime[i][0]);
    gtk_file_filter_set_name(filter, playlists_mime[i][1]);
    gtk_tree_store_append(store, &iter2, &iter1);
    gtk_tree_store_set(store, &iter2, 0, filter, -1);
    filters = g_slist_prepend(filters, filter);
    i++;
  }

  /* Video files */
  filter = gtk_file_filter_new();
  gtk_file_filter_set_name(filter, _("Video files"));
  i = 0;
  while (video_mime[i][0]) {
    gtk_file_filter_add_mime_type(filter, video_mime[i][0]);
    i++;
  }
  gtk_tree_store_append(store, &iter1, NULL);
  gtk_tree_store_set(store, &iter1, 0, filter, -1);
  filters = g_slist_prepend(filters, filter);

  /* Individual video files */
  i = 0;
  while (video_mime[i][0]) {
    filter = gtk_file_filter_new();
    gtk_file_filter_add_mime_type(filter, video_mime[i][0]);
    gtk_file_filter_set_name(filter, video_mime[i][1]);
    gtk_tree_store_append(store, &iter2, &iter1);
    gtk_tree_store_set(store, &iter2, 0, filter, -1);
    filters = g_slist_prepend(filters, filter);
    i++;
  }

  filters = g_slist_reverse(filters);

  for (f = filters; f; f = g_slist_next(f)) {
    g_object_ref(f->data);
  }

  return GTK_TREE_MODEL(store);
}


static void
gnac_ui_file_chooser_foreach(gpointer data,
                             gpointer user_data)
{
  GSList **list = (GSList **) user_data;
  GFile *file = g_file_new_for_uri((gchar *) data);
  *list = g_slist_prepend(*list, file);
  g_free(data);
}


static void
gnac_ui_file_chooser_set_last_used_folder(gchar *folder)
{
  g_free(last_used_folder);
  last_used_folder = folder;
}


static GtkWidget *
gnac_ui_file_chooser_new(void)
{
  gnac_file_chooser = gnac_ui_get_widget("gnac_file_chooser");
  GtkTreeModel *model = gnac_ui_file_chooser_get_filters_model();
  GtkWidget *combo = gnac_ui_get_widget("filters_combo");
  gtk_combo_box_set_model(GTK_COMBO_BOX(combo), model);

  GtkCellRenderer *renderer = gtk_cell_renderer_text_new();
  gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(combo), renderer, TRUE);
  gtk_cell_layout_set_cell_data_func(GTK_CELL_LAYOUT(combo), renderer,
      (GtkCellLayoutDataFunc) gnac_ui_file_chooser_cell_data_func,
      NULL, NULL);

  last_used_folder = g_get_current_dir();

  gnac_ui_reset_file_filter();

  return gnac_file_chooser;
}


static void
gnac_remove_track(gpointer data,
                  gpointer user_data)
{
  gchar *uri = NULL;
  GtkTreeRowReference *ref = (GtkTreeRowReference *) data;
  gnac_file_list_get(ref, &uri);

  GFile *file = g_file_new_for_uri(uri);
  GError *error = NULL;

  libgnac_converter_remove(converter, file, &error);
  if (error) {
    g_printerr("%s: %s\n", _("Error"), error->message);
    g_clear_error(&error);
  }

  g_object_unref(file);
  gtk_tree_row_reference_free(ref);
  g_free(uri);
}


static void
gnac_ui_show_toolbar(void)
{
  GtkToggleAction *view_toolbar = GTK_TOGGLE_ACTION(
      gnac_ui_get_action("view_toolbar_item"));
  gboolean visible = gnac_settings_ui_get_boolean(GNAC_KEY_TOOLBAR_VISIBLE);
  gtk_toggle_action_set_active(view_toolbar, visible);
  gnac_ui_set_widget_visible("main_toolbar", visible);
}


void
gnac_ui_reset_file_filter(void)
{
  GtkComboBox *combo = GTK_COMBO_BOX(gnac_ui_get_widget("filters_combo"));
  /* Use the 'Supported files' filter by default */
  gtk_combo_box_set_active(combo, 1);
  gnac_ui_on_filter_changed(gnac_file_chooser, combo);
}


static GtkWidget *
gnac_ui_get_file_chooser(void)
{
  if (G_UNLIKELY(!gnac_file_chooser)) {
    gnac_file_chooser = gnac_ui_file_chooser_new();
  }

  gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(gnac_file_chooser),
      last_used_folder);

  return gnac_file_chooser;
}


void
gnac_ui_on_filter_changed(GtkWidget   *file_chooser,
                          GtkComboBox *combo)
{
  GtkTreeIter iter;
  if (!gtk_combo_box_get_active_iter(combo, &iter)) return;

  GtkTreeModel *model = gtk_combo_box_get_model(combo);
  g_return_if_fail(model);

  gpointer filter;
  gtk_tree_model_get(model, &iter, 0, &filter, -1);
  if (GTK_IS_FILE_FILTER(filter)) {
    gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(file_chooser),
        GTK_FILE_FILTER(filter));
  }
}


static gboolean
gnac_ui_file_chooser_close_on_add_button_is_active(void)
{
  GtkWidget *button = gnac_ui_get_widget("close_on_add_button");
  return gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button));
}


void
gnac_ui_file_chooser_response_cb(GtkDialog *dialog,
                                 gint       response,
                                 gpointer   user_data)
{
  switch (response) {
    /* Add button */
    case GTK_RESPONSE_NONE: {
      GSList *files = NULL;
      GSList *paths = gtk_file_chooser_get_uris(GTK_FILE_CHOOSER(dialog));

      /* Add the different files */
      g_slist_foreach(paths, (GFunc) gnac_ui_file_chooser_foreach, &files);
      g_slist_free(paths);
      gnac_add_files(files);

      gchar *current_folder = gtk_file_chooser_get_current_folder(
          GTK_FILE_CHOOSER(dialog));
      gnac_ui_file_chooser_set_last_used_folder(current_folder);

      /* Do we have to close the file chooser? */
      if (!gnac_ui_file_chooser_close_on_add_button_is_active()) return;

      break;
    }

    case GTK_RESPONSE_CLOSE:
    default:
      break;
  }

  gtk_widget_hide(GTK_WIDGET(dialog));
}


void
gnac_ui_file_chooser_file_activated_cb(GtkFileChooser *chooser,
                                       gpointer        user_data)
{
  gnac_ui_file_chooser_response_cb(GTK_DIALOG(chooser),
      GTK_RESPONSE_NONE, NULL);
}


gboolean
gnac_ui_file_chooser_key_press_event_cb(GtkWidget   *widget,
                                        GdkEventKey *event,
                                        gpointer     user_data)
{
  if (gnac_ui_utils_event_is_return_key(event)) {
    gnac_ui_file_chooser_response_cb(GTK_DIALOG(widget),
        GTK_RESPONSE_NONE, NULL);
    return TRUE;
  }
  return FALSE;
}


static void
gnac_ui_new(void)
{
  gnac_stock_items_init();

  gnac_main_builder = gnac_ui_utils_create_gtk_builder(PKGDATADIR "/gnac.xml");

  gtk_window_set_default_icon_name(PACKAGE);

  gnac_profiles_init();

  GtkWidget *file_list = gnac_file_list_new();

  /* DnD portion code */
  gtk_drag_dest_set(file_list, GTK_DEST_DEFAULT_ALL,
      target_list, n_targets, GDK_ACTION_COPY);
}


void
gnac_ui_activate_profiles(gboolean activate)
{
  gtk_widget_set_sensitive(gnac_ui_get_widget("combo_profile"), activate);
  gtk_widget_set_sensitive(gnac_ui_get_widget("edit_profiles_btn"), activate);
}


void
gnac_ui_set_progress_fraction(gdouble fraction)
{
  GtkWidget *progress_bar = gnac_ui_get_widget("progressbar");
  gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(progress_bar), fraction);
}


void
gnac_ui_set_progress_text(const gchar *text)
{
  GtkWidget *progress_bar = gnac_ui_get_widget("progressbar");
  gtk_progress_bar_set_text(GTK_PROGRESS_BAR(progress_bar), text);
}


static void
gnac_ui_show_widget(const gchar *widget_name,
                    gboolean     show)
{
  GtkWidget *widget = gnac_ui_get_widget(widget_name);
  if (show) {
    gtk_widget_show(widget);
  } else {
    gtk_widget_hide(widget);
  }
}


static void
gnac_ui_show_pause(gboolean show)
{
  gnac_ui_show_widget("pause_button", show);
}


static void 
gnac_ui_show_progress(gboolean show)
{
  gnac_ui_show_widget("progressbar", show);
}


static gboolean 
gnac_ui_pulse_progress(void)
{
  if (state != GNAC_AUDIO_FILE_ACTION_STATE) return FALSE;

  GtkProgressBar *progress_bar = GTK_PROGRESS_BAR(
      gnac_ui_get_widget("progressbar"));
  gtk_progress_bar_pulse(progress_bar);

  gchar *progress_text = g_strdup_printf(
      ngettext("%u file added", "%u files added", nb_files_added),
      nb_files_added);
  gnac_ui_set_progress_text(progress_text);
  g_free(progress_text);

  return TRUE;
}


void
gnac_ui_push_status(const gchar *message)
{
  GtkStatusbar *status_bar = GTK_STATUSBAR(gnac_ui_get_widget("statusbar"));
  g_free(status_msg);
  status_msg = g_strdup(message);
  gtk_statusbar_pop(status_bar, 0);
  gtk_statusbar_push(status_bar, 0, status_msg);
}


void
gnac_ui_append_status(const gchar *message)
{
  GtkStatusbar *status_bar = GTK_STATUSBAR(gnac_ui_get_widget("statusbar"));
  gchar *old_msg = g_strdup(status_msg);
  g_free(status_msg);
  status_msg = g_strdup_printf("%s (%s)", old_msg, message);
  g_free(old_msg);
  gtk_statusbar_pop(status_bar, 0);
  gtk_statusbar_push(status_bar, 0, status_msg);
}


static void
gnac_ui_notify(const gchar *msg)
{
#ifdef HAVE_LIBNOTIFY

  NotifyNotification *notification = notify_notification_new(
      PACKAGE_NAME, msg, PACKAGE);

  notify_notification_set_timeout(notification, NOTIFY_EXPIRES_DEFAULT);
  notify_notification_set_urgency(notification, NOTIFY_URGENCY_NORMAL);

  notify_notification_show(notification, NULL);

#else /* HAVE_LIBNOTIFY */

  g_info("%s", msg);

#endif /* !HAVE_LIBNOTIFY */
}


void 
gnac_ui_show_popup_menu(GtkWidget      *treeview,
                        GdkEventButton *event, 
                        gpointer        user_data)
{
  GtkMenu *popup_menu = GTK_MENU(gnac_ui_get_widget("main_popup"));
  gtk_menu_popup(popup_menu, NULL, NULL, NULL, NULL,
      event ? event->button : 0, gdk_event_get_time((GdkEvent *) event));
}


void
gnac_ui_show_about_dialog(void)
{
  GtkWidget *about_dialog = gnac_ui_get_widget("aboutdialog");
  gtk_about_dialog_set_version(GTK_ABOUT_DIALOG(about_dialog), PACKAGE_VERSION);
  gtk_widget_show_all(about_dialog);
}


void
gnac_about_url_hook(GtkAboutDialog *dialog,
                    const gchar    *url,
                    gpointer        user_data)
{
  GError *error = NULL;
  if (!gtk_show_uri(gtk_widget_get_screen(GTK_WIDGET(dialog)),
                    url, gtk_get_current_event_time(), &error))
  {
    g_warning(_("Could not open link %s: %s"), url, error->message);
    g_clear_error(&error);
  }
}


GtkWidget *
gnac_ui_get_widget(const gchar *widget_name) 
{
  return gnac_ui_utils_get_widget(gnac_main_builder, widget_name);
}


GtkAction *
gnac_ui_get_action(const gchar *action_name)
{
  return gnac_ui_utils_get_action(gnac_main_builder, action_name);
}


void
gnac_ui_set_action_sensitive(const gchar *action_name,
                             gboolean     activate)
{
  gnac_ui_utils_set_action_sensitive(gnac_main_builder, action_name, activate);
}


void
gnac_ui_set_action_visible(const gchar *action_name,
                           gboolean     visible)
{
  gnac_ui_utils_set_action_visible(gnac_main_builder, action_name, visible);
}


void
gnac_ui_set_widget_sensitive(const gchar *widget_name,
                             gboolean     activate)
{
  gnac_ui_utils_set_widget_sensitive(gnac_main_builder, widget_name, activate);
}


void
gnac_ui_set_widget_visible(const gchar *widget_name,
                           gboolean     visible)
{
  gnac_ui_utils_set_widget_sensitive(gnac_main_builder, widget_name, visible);
}


void
gnac_ui_on_audio_empty_state(void)
{
  gnac_bars_on_row_deleted();
  gnac_bars_on_convert_stop();

  gnac_ui_hide_trayicon();
  gnac_ui_show_progress(FALSE);
  gnac_ui_show_pause(FALSE);

  if (timeout_id != 0) {
    g_source_remove(timeout_id);
    timeout_id = 0;
  }

  gnac_ui_set_progress_fraction(0.0f);
  gnac_ui_set_progress_text("");
}


void
gnac_ui_on_audio_file_action_state(void)
{
  timeout_id = g_timeout_add(100, (GSourceFunc) gnac_ui_pulse_progress, NULL);
  gnac_bars_on_add_files();
  gnac_ui_show_progress(TRUE);
}


void
gnac_ui_on_audio_ready_state(void)
{
  gnac_bars_on_row_inserted();

  if (gtk_window_has_toplevel_focus(
      GTK_WINDOW(gnac_ui_get_widget("main_window"))))
  {
    gnac_ui_hide_trayicon();
  }

  gnac_ui_show_progress(FALSE);
  gnac_ui_show_pause(FALSE);
  gnac_bars_on_convert_stop();

  if (timeout_id != 0) {
    g_source_remove(timeout_id);
    timeout_id = 0;
  }

  gnac_ui_set_progress_fraction(0.0f);
  gnac_ui_set_progress_text("");
}


void
gnac_ui_on_audio_convert_state(void)
{
  gnac_ui_show_trayicon();
  if (prev_state != GNAC_AUDIO_PAUSED_STATE) {
    gnac_file_list_hide_visual_bar();
  }
  gnac_ui_show_progress(TRUE);
  gnac_ui_show_pause(TRUE);
  gnac_bars_on_convert();
  gnac_bars_on_convert_resume();
}


gboolean
gnac_ui_on_focus_in_event_cb(GtkWidget     *widget,
                             GdkEventFocus *event,
                             gpointer       data)
{
  /* the trayicon is only displayed during a conversion */
  if (state == GNAC_AUDIO_READY_STATE) {
    gnac_ui_hide_trayicon();
  }
  return FALSE;
}


void
gnac_ui_on_clear_cb(GtkAction *action,
                    gpointer   data)
{
  libgnac_converter_clear(converter);
}


void
gnac_ui_on_view_toolbar_cb(GtkAction *action,
                           gpointer   user_data)
{
  GtkWidget *toolbar = gnac_ui_get_widget("main_toolbar");
  gboolean visible = !gtk_widget_get_visible(toolbar);
  gtk_widget_set_visible(toolbar, visible);
  gnac_settings_ui_set_boolean(GNAC_KEY_TOOLBAR_VISIBLE, visible);
}


void
gnac_ui_on_help_cb(GtkAction *action,
                   gpointer   user_data)
{
  gboolean ret;
  GError *error = NULL;

  ret = gtk_show_uri(gtk_widget_get_screen(gnac_ui_get_widget("main_window")),
      "help:gnac", gtk_get_current_event_time(), &error);
  if (!ret && error) {
    g_printerr("%s: %s\n", _("Could not display help"), error->message);
    g_clear_error(&error);
  }
}


void
gnac_ui_on_properties_cb(GtkAction *action,
                         gpointer   user_data)
{
  gnac_properties_window_show();
}


void
gnac_ui_on_add_cb(GtkAction *action,
                  gpointer   data)
{
  gtk_widget_show_all(gnac_ui_get_file_chooser());
}


void
gnac_ui_on_remove_cb(GtkAction *action,
                     gpointer   data)
{
  GList *rows = gnac_file_list_get_selected_rows();
  /* References are freed by gnac_remove_track */
  g_list_free_full(rows, (GDestroyNotify) gnac_remove_track);
}


void
gnac_ui_on_drag_data_received_cb(GtkWidget        *widget,
                                 GdkDragContext   *context,
                                 gint              x,
                                 gint              y,
                                 GtkSelectionData *selection_data,
                                 guint             info,
                                 guint             time,
                                 gpointer          data)
{
  /* Disable any filtering for DnD. */
  gnac_ui_reset_file_filter();

  gchar **uris = gtk_selection_data_get_uris(selection_data);

  if (!uris) {
    gtk_drag_finish(context, FALSE, FALSE, time);
    return;
  }

  guint index = 0;
  GSList *list_uris = NULL;

  gchar *uri = uris[index];
  while (uri) {
    GFile *file = g_file_new_for_uri(uri);
    list_uris = g_slist_prepend(list_uris, file);
    index++;
    uri = uris[index];
  }

  gnac_add_files(list_uris);

  gtk_drag_finish(context, TRUE, FALSE, time);
  g_strfreev(uris);
}


#ifdef HAVE_LIBUNIQUE
static UniqueResponse
gnac_ui_message_received_cb(UniqueApp         *app,
                            gint               command,
                            UniqueMessageData *message,
                            guint              time,
                            gpointer           user_data)
{
  GtkWindow *main_window = GTK_WINDOW(gnac_ui_get_widget("main_window"));

  switch (command)
  {
    case UNIQUE_ACTIVATE:
      gtk_widget_show_all(GTK_WIDGET(main_window));
      gtk_window_set_screen(main_window,
          unique_message_data_get_screen(message));
      gtk_window_present_with_time(main_window, time);
    break;

    case UNIQUE_CMD_ADD: {
      gchar **filenames = unique_message_data_get_uris(message);
      gnac_options_process_filenames(filenames);
      g_strfreev(filenames);
      gnac_ui_notify(_("Adding files..."));
      break;
    }

    case UNIQUE_CMD_DEBUG:
      gnac_ui_notify(_("Debug mode activated"));
      gnac_options_enable_debug();
      break;

    case UNIQUE_CMD_VERBOSE:
      gnac_ui_notify(_("Verbose mode activated"));
      gnac_options_enable_verbose();
      break;

    default:
      g_debug("Received unknown libunique command: %d", command);
      break;
  }

  return UNIQUE_RESPONSE_OK;
}
#endif /* HAVE_LIBUNIQUE */


static void
gnac_ui_init_notify(void)
{
#ifdef HAVE_LIBNOTIFY
  notify_init(PACKAGE_NAME);
#endif
}


static void
gnac_ui_init_unique(void)
{
#ifdef HAVE_LIBUNIQUE
  /* We only want a single instance of gnac to be running */
  app = unique_app_new_with_commands("org.gnome.Gnac", NULL,
      "add-files", UNIQUE_CMD_ADD,
      "debug"    , UNIQUE_CMD_DEBUG,
      "verbose"  , UNIQUE_CMD_VERBOSE,
      NULL);
  g_signal_connect(app, "message-received",
      G_CALLBACK(gnac_ui_message_received_cb), NULL);

  if (unique_app_is_running(app)) {
    g_info(_("An instance of Gnac is already running"));

    UniqueResponse response;

    /* Give the focus to the running instance */
    response = unique_app_send_message(app, UNIQUE_ACTIVATE, NULL);

    /* Transmit the debug option */
    if (options.debug) {
      response = unique_app_send_message(app, UNIQUE_CMD_DEBUG, NULL);
      if (response != UNIQUE_RESPONSE_OK) {
        g_warning(_("Failed to transmit the debug option"));
      }
    /* Transmit the verbose option */
    } else if (options.verbose) {
      response = unique_app_send_message(app, UNIQUE_CMD_VERBOSE, NULL);
      if (response != UNIQUE_RESPONSE_OK) {
        g_warning(_("Failed to transmit the verbose option"));
      }
    }

    /* Transmit filenames */
    if (options.filenames) {
      UniqueMessageData *message = unique_message_data_new();
      gchar **uris = gnac_utils_get_filenames_from_cmd_line(options.filenames);
      g_strfreev(options.filenames);
      if (!unique_message_data_set_uris(message, uris)) {
        g_warning(_("Failed to convert some uris"));
      }
      g_strfreev(uris);
      response = unique_app_send_message(app, UNIQUE_CMD_ADD, message);
      unique_message_data_free(message);
      if (response != UNIQUE_RESPONSE_OK) {
        g_warning(_("Failed to transmit filenames"));
      } else {
        g_info(_("Filenames transmitted to the running instance"));
      }
    }

    g_object_unref(app);

    gnac_exit(response == UNIQUE_RESPONSE_OK);
  }
#endif /* HAVE_LIBUNIQUE */
}


void
gnac_ui_init(void)
{
  gnac_ui_init_notify();
  gnac_ui_init_unique();
}


void 
gnac_ui_show(void)
{
  if (!gnac_main_builder) {
    gnac_ui_new();
  }

  GtkWidget *main_window = gnac_ui_get_widget("main_window");

#ifdef HAVE_LIBUNIQUE
  unique_app_watch_window(app, GTK_WINDOW(main_window));
#endif

  gtk_widget_show_all(main_window);

  gnac_ui_show_toolbar();
  gnac_ui_show_progress(FALSE);
  gnac_ui_show_pause(FALSE);
  gnac_bars_activate_pause(FALSE);
}


void
gnac_ui_destroy(void)
{
  gnac_properties_destroy();
  gnac_prefs_destroy();
  gnac_profiles_mgr_destroy();

  gnac_file_list_destroy();

  if (G_IS_OBJECT(trayicon)) {
    g_object_unref(trayicon);
    trayicon = NULL;
    if (tooltip_path) {
      g_free(tooltip_path);
      tooltip_path = NULL;
    }
  }

  if (G_IS_OBJECT(gnac_main_builder)) {
    GtkWidget *main_window = gnac_ui_get_widget("main_window");
    if (GTK_IS_WIDGET(main_window)) gtk_widget_destroy(main_window);

    GtkWidget *about_dialog = gnac_ui_get_widget("aboutdialog");
    if (GTK_IS_WIDGET(about_dialog)) gtk_widget_destroy(about_dialog);

    gnac_ui_file_chooser_dispose();
    g_object_unref(gnac_main_builder); 
    gnac_main_builder = NULL;
  }

  g_free(status_msg);
}


gboolean
gnac_ui_confirm_exit(void)
{
  /* disable stop/resume and quit from trayicon's menu */
  gnac_ui_trayicon_menu_activate(FALSE);

  GtkWidget *dialog = gtk_message_dialog_new(
      GTK_WINDOW(gnac_ui_get_widget("main_window")), GTK_DIALOG_MODAL,
      GTK_MESSAGE_WARNING, GTK_BUTTONS_YES_NO, "%s\n%s",
      _("A conversion is currently running..."),
      _("Are you sure you want to quit?"));
  gtk_window_set_title(GTK_WINDOW(dialog), PACKAGE_NAME);

  gint response = gtk_dialog_run(GTK_DIALOG(dialog));

  gtk_widget_destroy(dialog);

  /* enable stop/resume and quit from trayicon's menu */
  gnac_ui_trayicon_menu_activate(TRUE);

  return (response == GTK_RESPONSE_YES);
}


void
gnac_ui_on_conversion_completed(const gchar *msg)
{
  gnac_ui_notify(msg);
  gnac_ui_push_status(msg);
  gnac_ui_trayicon_tooltip_update(msg);
  gnac_ui_set_action_visible("tray_stop_item", FALSE);
  gnac_ui_set_action_visible("tray_pause_item", FALSE);
  gnac_ui_update_trayicon_popup();
}


static gboolean
gnac_ui_query_tooltip_cb(GtkStatusIcon *status_icon,
                         gint           x,
                         gint           y,
                         gboolean       keyboard_mode,
                         GtkTooltip    *tooltip,
                         gpointer       user_data)
{
  GError *error = NULL;
  GFile *tooltip_file = g_file_new_for_uri(tooltip_path);
  LibgnacTags *tags = libgnac_metadata_extract(metadata, tooltip_file, &error);
  if (error) {
    g_debug("Failed to extract metadata for %s: %s",
        tooltip_path, error->message);
    g_clear_error(&error);
  }
  g_object_unref(tooltip_file);

  const gchar *album = libgnac_metadata_tag_exists(tags, GST_TAG_ALBUM) ?
      g_value_get_string(LIBGNAC_METADATA_TAG_ALBUM(tags)) : NULL;
  const gchar *artist = libgnac_metadata_tag_exists(tags, GST_TAG_ARTIST) ?
      g_value_get_string(LIBGNAC_METADATA_TAG_ARTIST(tags)) : NULL;
  const gchar *title = libgnac_metadata_tag_exists(tags, GST_TAG_TITLE) ?
      g_value_get_string(LIBGNAC_METADATA_TAG_TITLE(tags)) : NULL;

  gchar *text;

  if (title)
  {
    text = g_markup_printf_escaped("  <b>%s</b>  \n"
        "  <span color=\"#888\">%s</span> %s  \n"
        "  <span color=\"#888\">%s</span> %s  ",
        title,
        /* Translators: title by artist from album */
        _("by"), artist ? artist : _("Unknown Artist"),
        /* Translators: title by artist from album */
        _("from"), album ? album : _("Unknown Album"));
  }
  else
  {
    gchar *name = gnac_utils_get_display_name(tooltip_path, NULL);
    if (name) {
      text = g_markup_printf_escaped("  <b>%s</b>  ", name);
    } else {
      text = g_strdup(tooltip_path);
    }
    g_free(name);
  }

  gtk_tooltip_set_markup(tooltip, text);
  g_free(text);

  /* check wether we have a cover to display */
  if (libgnac_metadata_tag_exists(tags, GST_TAG_IMAGE)) {
    GdkPixbuf *pixbuf = g_value_get_object(LIBGNAC_METADATA_TAG_IMAGE(tags));
    pixbuf = gnac_ui_utils_scale_pixbuf(pixbuf, 64, 64);
    pixbuf = gnac_ui_utils_add_border_to_pixbuf(pixbuf);
    gtk_tooltip_set_icon(tooltip, pixbuf);
    g_object_unref(pixbuf);
  }

  return TRUE;
}


void
gnac_ui_show_trayicon(void)
{
  if (!gnac_settings_ui_get_boolean(GNAC_KEY_TRAY_ICON)) return;

  GtkWidget *main_window = gnac_ui_get_widget("main_window");

  if (!trayicon) {
    trayicon = gtk_status_icon_new_from_icon_name("gnac");
    gtk_status_icon_set_has_tooltip(trayicon, TRUE);
    
    g_signal_connect(G_OBJECT(trayicon), "query-tooltip",
        G_CALLBACK(gnac_ui_query_tooltip_cb), NULL);
    g_signal_connect(G_OBJECT(trayicon), "activate", 
        G_CALLBACK(gnac_ui_on_trayicon), NULL);
    g_signal_connect(G_OBJECT(trayicon), "popup-menu", 
        G_CALLBACK(gnac_ui_on_trayicon_popup), NULL);
  }
  
  gtk_window_get_position(GTK_WINDOW(main_window), &root_x, &root_y);
  gtk_status_icon_set_visible(trayicon, TRUE);
}


static gboolean
gnac_ui_main_window_is_visible(void)
{
  gboolean visible;
  GtkWidget *main_window = gnac_ui_get_widget("main_window");
  g_object_get(main_window, "visible", &visible, NULL);
  return visible;
}


static void
gnac_ui_update_trayicon_popup(void)
{
  GtkAction *show_action = gnac_ui_get_action("tray_show_hide_item");
  /* Translators: Show/Hide main window */
  gchar *label_text = g_strdup((gnac_ui_main_window_is_visible() &&
      state != GNAC_AUDIO_READY_STATE) ? _("Hide") : _("Show"));

  /* update the menu */
  gtk_action_set_label(show_action, label_text);

  g_free(label_text);
}


void
gnac_ui_hide_trayicon(void)
{  
  if (!gnac_settings_ui_get_boolean(GNAC_KEY_TRAY_ICON) || !trayicon) return;

  GtkWidget *main_window = gnac_ui_get_widget("main_window");

  if (!gnac_ui_main_window_is_visible()) {
    gtk_window_move(GTK_WINDOW(main_window), root_x, root_y);
    gtk_widget_show_all(main_window);
  }

  gtk_status_icon_set_visible(trayicon, FALSE);
}


void
gnac_ui_on_trayicon(GtkStatusIcon *trayicon,
                    gpointer       data)
{
  GtkWidget *main_window = gnac_ui_get_widget("main_window");

  if (gnac_ui_main_window_is_visible()) {
    gtk_window_get_position(GTK_WINDOW(main_window), &root_x, &root_y);
    gtk_widget_hide(main_window);
  } else {
    gtk_window_move(GTK_WINDOW(main_window), root_x, root_y);
    gtk_widget_show_all(main_window);
  }

  if (state == GNAC_AUDIO_READY_STATE) {
    gnac_ui_hide_trayicon();
  }
}


void
gnac_ui_on_trayicon_popup(GtkStatusIcon *trayicon,
                          guint          button,
                          guint          activate_time,
                          gpointer       data)
{
  /* update the menu and then display it */
  gnac_ui_update_trayicon_popup();
  GtkMenu *trayicon_menu = GTK_MENU(gnac_ui_get_widget("tray_popup"));
  gtk_menu_popup(trayicon_menu, NULL, NULL, NULL, NULL, button, activate_time);
}


void
gnac_ui_trayicon_tooltip_update(const gchar *tooltip)
{
  if (!gnac_settings_ui_get_boolean(GNAC_KEY_TRAY_ICON) || !trayicon) return;

  g_free(tooltip_path);
  tooltip_path = g_strdup(tooltip);

  GdkDisplay *display = gdk_display_get_default();
  if (display) gtk_tooltip_trigger_tooltip_query(display);
}


void
gnac_ui_trayicon_menu_activate(gboolean activate)
{
  gnac_ui_set_action_sensitive("tray_pause_item", activate);
  gnac_ui_set_action_sensitive("tray_stop_item", activate);
  gnac_ui_set_action_sensitive("tray_quit_item", activate);
}


void
gnac_ui_trayicon_menu_stop(gpointer data,
                           gpointer user_data)
{
  GtkWidget *main_window = gnac_ui_get_widget("main_window");

  if (!gnac_ui_main_window_is_visible()) {
    gtk_window_move(GTK_WINDOW(main_window), root_x, root_y);
    gtk_widget_show_all(main_window);
  }

  gnac_ui_hide_trayicon();
  gnac_on_ui_convert_cb(NULL, NULL);
}


void
gnac_ui_trayicon_menu_pause(gpointer data,
                            gpointer user_data)
{
  gnac_on_ui_pause_cb(NULL, NULL);
}


gint
gnac_ui_show_error_trash(const gchar *filename)
{
  GtkWindow *main_window = GTK_WINDOW(gnac_ui_get_widget("main_window"));
  GtkWidget *dialog = gtk_message_dialog_new(main_window,
      GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_QUESTION, GTK_BUTTONS_CANCEL,
      _("Cannot move file to trash, do you\nwant to delete immediately?"));
  gtk_dialog_add_button(GTK_DIALOG(dialog), GTK_STOCK_DELETE, 1);
  gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(dialog),
      _("The file \"%s\" cannot be moved to the trash."), filename);

  gdk_threads_enter();
    gint response = gtk_dialog_run(GTK_DIALOG(dialog));
  gdk_threads_leave();

  gtk_widget_destroy(dialog);

  return response;
}
