/* vi:set et ai sw=2 sts=2 ts=2: */
/*-
 * Copyright (c) 2005-2006 Benedikt Meurer <benny@xfce.org>.
 * Copyright (c) 2009 Jannis Pohlmann <jannis@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 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

#ifdef HAVE_MEMORY_H
#include <memory.h>
#endif
#ifdef HAVE_STRING_H
#include <string.h>
#endif

#include <thunar/thunar-chooser-model.h>
#include <thunar/thunar-gobject-extensions.h>
#include <thunar/thunar-icon-factory.h>
#include <thunar/thunar-private.h>



/* Property identifiers */
enum
{
  PROP_0,
  PROP_CONTENT_TYPE,
};




static void     thunar_chooser_model_constructed    (GObject                  *object);
static void     thunar_chooser_model_finalize       (GObject                  *object);
static void     thunar_chooser_model_get_property   (GObject                  *object,
                                                     guint                     prop_id,
                                                     GValue                   *value,
                                                     GParamSpec               *pspec);
static void     thunar_chooser_model_set_property   (GObject                  *object,
                                                     guint                     prop_id,
                                                     const GValue             *value,
                                                     GParamSpec               *pspec);
static void     thunar_chooser_model_reload         (ThunarChooserModel       *model);



struct _ThunarChooserModelClass
{
  GtkTreeStoreClass __parent__;
};

struct _ThunarChooserModel
{
  GtkTreeStore __parent__;

  gchar *content_type;
};



G_DEFINE_TYPE (ThunarChooserModel, thunar_chooser_model, GTK_TYPE_TREE_STORE)



static void
thunar_chooser_model_class_init (ThunarChooserModelClass *klass)
{
  GObjectClass *gobject_class;

  gobject_class = G_OBJECT_CLASS (klass);
  gobject_class->finalize = thunar_chooser_model_finalize;
  gobject_class->constructed = thunar_chooser_model_constructed;
  gobject_class->get_property = thunar_chooser_model_get_property;
  gobject_class->set_property = thunar_chooser_model_set_property;

  /**
   * ThunarChooserModel::content-type:
   *
   * The content type for the model.
   **/
  g_object_class_install_property (gobject_class,
                                   PROP_CONTENT_TYPE,
                                   g_param_spec_string ("content-type", 
                                                        "content-type", 
                                                        "content-type",
                                                        NULL,
                                                        G_PARAM_CONSTRUCT_ONLY | 
                                                        EXO_PARAM_READWRITE));
}



static void
thunar_chooser_model_init (ThunarChooserModel *model)
{
  /* allocate the types array for the columns */
  GType column_types[THUNAR_CHOOSER_MODEL_N_COLUMNS] =
  {
    G_TYPE_STRING,
    G_TYPE_ICON,
    G_TYPE_APP_INFO,
    PANGO_TYPE_STYLE,
    PANGO_TYPE_WEIGHT,
  };

  /* register the column types */
  gtk_tree_store_set_column_types (GTK_TREE_STORE (model), 
                                   G_N_ELEMENTS (column_types), 
                                   column_types);
}



static void
thunar_chooser_model_constructed (GObject *object)
{
  ThunarChooserModel *model = THUNAR_CHOOSER_MODEL (object);

  /* start to load the applications installed on the system */
  thunar_chooser_model_reload (model);
}



static void
thunar_chooser_model_finalize (GObject *object)
{
  ThunarChooserModel *model = THUNAR_CHOOSER_MODEL (object);

  /* free the content type */
  g_free (model->content_type);

  (*G_OBJECT_CLASS (thunar_chooser_model_parent_class)->finalize) (object);
}



static void
thunar_chooser_model_get_property (GObject    *object,
                                   guint       prop_id,
                                   GValue     *value,
                                   GParamSpec *pspec)
{
  ThunarChooserModel *model = THUNAR_CHOOSER_MODEL (object);

  switch (prop_id)
    {
    case PROP_CONTENT_TYPE:
      g_value_set_string (value, thunar_chooser_model_get_content_type (model));
      break;

    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
    }
}



static void
thunar_chooser_model_set_property (GObject      *object,
                                   guint         prop_id,
                                   const GValue *value,
                                   GParamSpec   *pspec)
{
  ThunarChooserModel *model = THUNAR_CHOOSER_MODEL (object);

  switch (prop_id)
    {
    case PROP_CONTENT_TYPE:
      model->content_type = g_value_dup_string (value);
      break;

    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
    }
}



static void
thunar_chooser_model_append (ThunarChooserModel *model,
                             const gchar        *title,
                             const gchar        *icon_name,
                             GList              *app_infos)
{
  GtkTreeIter child_iter;
  GtkTreeIter parent_iter;
  GIcon      *icon;
  GList      *lp;
  gboolean    inserted_infos = FALSE;

  _thunar_return_if_fail (THUNAR_IS_CHOOSER_MODEL (model));
  _thunar_return_if_fail (title != NULL);
  _thunar_return_if_fail (icon_name != NULL);

  icon = g_themed_icon_new (icon_name);

  gtk_tree_store_append (GTK_TREE_STORE (model), &parent_iter, NULL);
  gtk_tree_store_set (GTK_TREE_STORE (model), &parent_iter,
                      THUNAR_CHOOSER_MODEL_COLUMN_NAME, title,
                      THUNAR_CHOOSER_MODEL_COLUMN_ICON, icon,
                      THUNAR_CHOOSER_MODEL_COLUMN_WEIGHT, PANGO_WEIGHT_BOLD,
                      -1);

  g_object_unref (icon);

  if (G_LIKELY (app_infos != NULL))
    {
      /* insert the program items */
      for (lp = app_infos; lp != NULL; lp = lp->next)
        {
          if (!thunar_g_app_info_should_show (lp->data))
            continue;

          /* append the tree row with the program data */
          gtk_tree_store_append (GTK_TREE_STORE (model), &child_iter, &parent_iter);
          gtk_tree_store_set (GTK_TREE_STORE (model), &child_iter,
                              THUNAR_CHOOSER_MODEL_COLUMN_NAME, g_app_info_get_name (lp->data),
                              THUNAR_CHOOSER_MODEL_COLUMN_ICON, g_app_info_get_icon (lp->data),
                              THUNAR_CHOOSER_MODEL_COLUMN_APPLICATION, lp->data,
                              THUNAR_CHOOSER_MODEL_COLUMN_WEIGHT, PANGO_WEIGHT_NORMAL,
                              -1);

          inserted_infos = TRUE;
        }
    }
  
  if (!inserted_infos)
    {
      /* tell the user that we don't have any applications for this category */
      gtk_tree_store_append (GTK_TREE_STORE (model), &child_iter, &parent_iter);
      gtk_tree_store_set (GTK_TREE_STORE (model), &child_iter,
                          THUNAR_CHOOSER_MODEL_COLUMN_NAME, _("None available"),
                          THUNAR_CHOOSER_MODEL_COLUMN_STYLE, PANGO_STYLE_ITALIC,
                          THUNAR_CHOOSER_MODEL_COLUMN_WEIGHT, PANGO_WEIGHT_NORMAL,
                          -1);
    }
}



static gint
compare_app_infos (gconstpointer a,
                   gconstpointer b)
{
  return g_app_info_equal (G_APP_INFO (a), G_APP_INFO (b)) ? 0 : 1;
}



static gint
sort_app_infos (gconstpointer a,
                gconstpointer b)
{
  return g_utf8_collate (g_app_info_get_name (G_APP_INFO (a)),
                         g_app_info_get_name (G_APP_INFO (b)));
}



static void
thunar_chooser_model_reload (ThunarChooserModel *model)
{
  GList *all;
  GList *lp;
  GList *other = NULL;
  GList *recommended;

  _thunar_return_if_fail (THUNAR_IS_CHOOSER_MODEL (model));
  _thunar_return_if_fail (model->content_type != NULL);

  gtk_tree_store_clear (GTK_TREE_STORE (model));

  /* check if we have any applications for this type */
  recommended = g_app_info_get_all_for_type (model->content_type);

  /* append them as recommended */
  recommended = g_list_sort (recommended, sort_app_infos);
  thunar_chooser_model_append (model, 
                               _("Recommended Applications"), 
                               "preferences-desktop-default-applications", 
                               recommended);

  all = g_app_info_get_all ();
  for (lp = all; lp != NULL; lp = lp->next)
    {
      if (g_list_find_custom (recommended,
                              lp->data,
                              compare_app_infos) == NULL)
        {
          other = g_list_prepend (other, lp->data);
        }
    }

  /* append the other applications */
  other = g_list_sort (other, sort_app_infos);
  thunar_chooser_model_append (model,
                               _("Other Applications"),
                               "gnome-applications",
                               other);

  g_list_free_full (recommended, g_object_unref);
  g_list_free_full (all, g_object_unref);

  g_list_free (other);
}



/**
 * thunar_chooser_model_new:
 * @content_type : the content type for this model.
 *
 * Allocates a new #ThunarChooserModel for @content_type.
 *
 * Return value: the newly allocated #ThunarChooserModel.
 **/
ThunarChooserModel *
thunar_chooser_model_new (const gchar *content_type)
{
  return g_object_new (THUNAR_TYPE_CHOOSER_MODEL,
                       "content-type", content_type,
                       NULL);
}



/**
 * thunar_chooser_model_get_content_type:
 * @model : a #ThunarChooserModel.
 *
 * Returns the content type for @model.
 *
 * Return value: the content type for @model.
 **/
const gchar *
thunar_chooser_model_get_content_type (ThunarChooserModel *model)
{
  _thunar_return_val_if_fail (THUNAR_IS_CHOOSER_MODEL (model), NULL);
  return model->content_type;
}



/**
 * thunar_chooser_model_remove:
 * @model : a #ThunarChooserModel.
 * @iter  : the #GtkTreeIter for the application to remove.
 * @error : return location for errors or %NULL.
 *
 * Tries to remove the application at the specified @iter from
 * the systems application database. Returns %TRUE on success,
 * otherwise %FALSE is returned.
 *
 * Return value: %TRUE on success, %FALSE otherwise.
 **/
gboolean
thunar_chooser_model_remove (ThunarChooserModel *model,
                             GtkTreeIter        *iter,
                             GError            **error)
{
  GAppInfo *app_info;
  gboolean  succeed;

  _thunar_return_val_if_fail (THUNAR_IS_CHOOSER_MODEL (model), FALSE);
  _thunar_return_val_if_fail (error == NULL || *error == NULL, FALSE);
  _thunar_return_val_if_fail (gtk_tree_store_iter_is_valid (GTK_TREE_STORE (model), iter), FALSE);

  /* determine the app info for the iter */
  gtk_tree_model_get (GTK_TREE_MODEL (model), 
                      iter, 
                      THUNAR_CHOOSER_MODEL_COLUMN_APPLICATION, &app_info, 
                      -1);

  if (G_UNLIKELY (app_info == NULL))
    return TRUE;

  /* try to remove support for this content type */
  succeed = g_app_info_remove_supports_type (app_info,
                                             model->content_type,
                                             error);

  /* try to delete the file */
  if (succeed && g_app_info_delete (app_info))
    {
      g_set_error (error, G_IO_ERROR,
                   G_IO_ERROR_FAILED,
                   _("Failed to remove \"%s\"."),
                   g_app_info_get_id (app_info));
    }

  /* clean up */
  g_object_unref (app_info);

  /* if the removal was successfull, delete the row from the model */
  if (G_LIKELY (succeed))
    gtk_tree_store_remove (GTK_TREE_STORE (model), iter);

  return succeed;
}


