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

templates.c

/*
 * This file is part of LaTeXila.
 *
 * Copyright © 2009, 2010 Sébastien Wilmet
 *
 * LaTeXila 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.
 *
 * LaTeXila 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 LaTeXila.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <stdlib.h>
#include <gtk/gtk.h>
#include <sys/stat.h> // for S_IRWXU
#include <glib/gstdio.h> // for g_remove()

#include "main.h"
#include "config.h"
#include "print.h"
#include "callbacks.h"
#include "templates.h"

static void add_template_from_string (GtkListStore *store, const gchar *name,
            const gchar *icon_id, const gchar *contents);
static void add_template_from_file (GtkListStore *store, const gchar *name,
            const gchar *icon_id, const gchar *path);
static GtkWidget * create_icon_view (GtkListStore *store);
static void cb_icon_view_selection_changed (GtkIconView *icon_view,
            gpointer other_icon_view);
static gchar * get_rc_file (void);
static gchar * get_rc_dir (void);
static void add_personnal_template (const gchar *contents);
static void save_rc_file (void);
static void save_contents (void);

static GtkListStore *default_store;
static GtkListStore *personnal_store;
static gint nb_personnal_templates;

void
cb_new (void)
{
      GtkWidget *dialog = gtk_dialog_new_with_buttons (_("New File..."),
                  latexila.main_window,
                  GTK_DIALOG_NO_SEPARATOR,
                  GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
                  GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
                  NULL);

      gtk_window_set_default_size (GTK_WINDOW (dialog), 400, 330);
      
      GtkWidget *content_area = gtk_dialog_get_content_area (GTK_DIALOG (dialog));

      /* icon view for the default templates */
      GtkWidget *icon_view_default_templates = create_icon_view (default_store);

      // with a scrollbar (without that there is a problem for resizing the
      // dialog, we can make it bigger but not smaller...)
      GtkWidget *scrollbar = gtk_scrolled_window_new (NULL, NULL);
      gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrollbar),
                  GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
      gtk_container_add (GTK_CONTAINER (scrollbar), icon_view_default_templates);

      // with a frame
      GtkWidget *frame = gtk_frame_new (_("Default templates"));
      gtk_container_add (GTK_CONTAINER (frame), scrollbar);

      gtk_box_pack_start (GTK_BOX (content_area), frame, TRUE, TRUE, 10);

      /* icon view for the personnal templates */
      GtkWidget *icon_view_personnal_templates = create_icon_view (personnal_store);

      // with a scrollbar
      scrollbar = gtk_scrolled_window_new (NULL, NULL);
      gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrollbar),
                  GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
      gtk_container_add (GTK_CONTAINER (scrollbar), icon_view_personnal_templates);

      // with a frame
      frame = gtk_frame_new (_("Your personnal templates"));
      gtk_container_add (GTK_CONTAINER (frame), scrollbar);

      gtk_box_pack_start (GTK_BOX (content_area), frame, TRUE, TRUE, 10);

      gtk_widget_show_all (content_area);

      /* signals */
      g_signal_connect (G_OBJECT (icon_view_default_templates),
                  "selection-changed",
                  G_CALLBACK (cb_icon_view_selection_changed),
                  GTK_ICON_VIEW (icon_view_personnal_templates));

      g_signal_connect (G_OBJECT (icon_view_personnal_templates),
                  "selection-changed",
                  G_CALLBACK (cb_icon_view_selection_changed),
                  GTK_ICON_VIEW (icon_view_default_templates));

      if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT)
      {
            GList *selected_items = gtk_icon_view_get_selected_items (
                        GTK_ICON_VIEW (icon_view_default_templates));
            GtkTreeModel *model = GTK_TREE_MODEL (default_store);

            // if no item is selected in the default templates, maybe one item is
            // selected in the personnal templates
            if (g_list_length (selected_items) == 0)
            {
                  // free the GList
                  g_list_foreach (selected_items, (GFunc) gtk_tree_path_free, NULL);
                  g_list_free (selected_items);

                  selected_items = gtk_icon_view_get_selected_items (
                              GTK_ICON_VIEW (icon_view_personnal_templates));
                  model = GTK_TREE_MODEL (personnal_store);
            }

            GtkTreePath *path = g_list_nth_data (selected_items, 0);
            GtkTreeIter iter;
            gchar *contents = NULL;

            if (path != NULL && gtk_tree_model_get_iter (model, &iter, path))
            {
                  gtk_tree_model_get (model, &iter,
                              COL_TEMPLATE_CONTENTS, &contents,
                              -1);
            }
            else
                  contents = g_strdup ("");

            create_document_in_new_tab (NULL, contents, _("New document"));

            g_free (contents);

            // free the GList
            g_list_foreach (selected_items, (GFunc) gtk_tree_path_free, NULL);
            g_list_free (selected_items);
      }

      gtk_widget_destroy (dialog);
}

void
cb_create_template (void)
{
      if (latexila.active_doc == NULL)
            return;

      GtkWidget *dialog = gtk_dialog_new_with_buttons (_("New Template..."),
                  latexila.main_window, 0,
                  GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
                  GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
                  NULL);

      gtk_window_set_default_size (GTK_WINDOW (dialog), 400, 330);

      GtkWidget *content_area = gtk_dialog_get_content_area (GTK_DIALOG (dialog));

      /* name */
      GtkWidget *hbox = gtk_hbox_new (FALSE, 5);
      GtkWidget *label = gtk_label_new (_("Name of the new template:"));
      GtkWidget *entry = gtk_entry_new ();

      gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
      gtk_box_pack_start (GTK_BOX (hbox), entry, FALSE, FALSE, 0);
      gtk_box_pack_start (GTK_BOX (content_area), hbox, FALSE, FALSE, 10);

      /* icon */
      // we take the default store because it contains all the icons
      GtkWidget *icon_view = create_icon_view (default_store);

      // bug, see https://bugzilla.gnome.org/show_bug.cgi?id=608609
      //gtk_icon_view_set_text_column (GTK_ICON_VIEW (icon_view), -1);

      // with a scrollbar
      GtkWidget *scrollbar = gtk_scrolled_window_new (NULL, NULL);
      gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrollbar),
                  GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
      gtk_container_add (GTK_CONTAINER (scrollbar), icon_view);

      // with a frame
      GtkWidget *frame = gtk_frame_new (_("Choose an icon:"));
      gtk_container_add (GTK_CONTAINER (frame), scrollbar);
      gtk_box_pack_start (GTK_BOX (content_area), frame, TRUE, TRUE, 10);

      gtk_widget_show_all (content_area);

      while (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT)
      {
            // if no name specified
            if (gtk_entry_get_text_length (GTK_ENTRY (entry)) == 0)
                  continue;

            GList *selected_items =
                  gtk_icon_view_get_selected_items (GTK_ICON_VIEW (icon_view));

            // if no icon selected
            if (g_list_length (selected_items) == 0)
            {
                  g_list_free (selected_items);
                  continue;
            }

            nb_personnal_templates++;

            // get the contents
            GtkTextBuffer *buffer =
                  GTK_TEXT_BUFFER (latexila.active_doc->source_buffer);
            GtkTextIter start, end;
            gtk_text_buffer_get_bounds (buffer, &start, &end);
            gchar *contents = gtk_text_buffer_get_text (buffer, &start, &end,
                        FALSE);

            // get the name
            const gchar *name = gtk_entry_get_text (GTK_ENTRY (entry));

            // get the icon id
            GtkTreeModel *model = GTK_TREE_MODEL (default_store);
            GtkTreePath *path = g_list_nth_data (selected_items, 0);
            GtkTreeIter iter;
            gchar *icon_id;
            gtk_tree_model_get_iter (model, &iter, path);
            gtk_tree_model_get (model, &iter,
                        COL_TEMPLATE_ICON_ID, &icon_id,
                        -1);

            add_template_from_string (personnal_store, name, icon_id, contents);
            add_personnal_template (contents);

            g_free (contents);
            g_free (icon_id);
            g_list_foreach (selected_items, (GFunc) gtk_tree_path_free, NULL);
            g_list_free (selected_items);
            break;
      }

      gtk_widget_destroy (dialog);
}

void
cb_delete_template (void)
{
      GtkWidget *dialog = gtk_dialog_new_with_buttons (_("Delete Template(s)..."),
                  latexila.main_window,
                  GTK_DIALOG_NO_SEPARATOR,
                  GTK_STOCK_DELETE, GTK_RESPONSE_ACCEPT,
                  GTK_STOCK_OK, GTK_RESPONSE_REJECT,
                  NULL);

      gtk_window_set_default_size (GTK_WINDOW (dialog), 400, 200);
      
      GtkWidget *content_area = gtk_dialog_get_content_area (GTK_DIALOG (dialog));

      /* icon view for the personnal templates */
      GtkWidget *icon_view = create_icon_view (personnal_store);
      gtk_icon_view_set_selection_mode (GTK_ICON_VIEW (icon_view),
                  GTK_SELECTION_MULTIPLE);

      // with a scrollbar (without that there is a problem for resizing the
      // dialog, we can make it bigger but not smaller...)
      GtkWidget *scrollbar = gtk_scrolled_window_new (NULL, NULL);
      gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrollbar),
                  GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
      gtk_container_add (GTK_CONTAINER (scrollbar), icon_view);

      // with a frame
      GtkWidget *frame = gtk_frame_new (_("Personnal templates"));
      gtk_container_add (GTK_CONTAINER (frame), scrollbar);

      gtk_box_pack_start (GTK_BOX (content_area), frame, TRUE, TRUE, 10);

      gtk_widget_show_all (content_area);

      gint nb_personnal_templates_before = nb_personnal_templates;

      while (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT)
      {
            GList *selected_items = gtk_icon_view_get_selected_items (
                        GTK_ICON_VIEW (icon_view));
            GtkTreeModel *model = GTK_TREE_MODEL (personnal_store);
            
            guint nb_selected_items = g_list_length (selected_items);

            for (gint i = 0 ; i < nb_selected_items ; i++)
            {
                  GtkTreePath *path = g_list_nth_data (selected_items, i);
                  GtkTreeIter iter;
                  gtk_tree_model_get_iter (model, &iter, path);
                  gtk_list_store_remove (personnal_store, &iter);
            }

            nb_personnal_templates -= nb_selected_items;

            // free the GList
            g_list_foreach (selected_items, (GFunc) gtk_tree_path_free, NULL);
            g_list_free (selected_items);
      }

      if (nb_personnal_templates != nb_personnal_templates_before)
      {
            save_rc_file ();
            save_contents ();
      }

      gtk_widget_destroy (dialog);
}

void
init_templates (void)
{
      /* default templates */
      default_store = gtk_list_store_new (N_COLS_TEMPLATE,
                  GDK_TYPE_PIXBUF, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING);

      add_template_from_string (default_store, _("Empty"), "empty", "");

      // article
      gchar *path = g_strdup_printf (DATA_DIR "/templates/%s",
                  _("article-en.tex"));
      add_template_from_file (default_store, _("Article"), "article", path);
      g_free (path);

      // report
      path = g_strdup_printf (DATA_DIR "/templates/%s",
                  _("report-en.tex"));
      add_template_from_file (default_store, _("Report"), "report", path);
      g_free (path);

      // book
      path = g_strdup_printf (DATA_DIR "/templates/%s",
                  _("book-en.tex"));
      add_template_from_file (default_store, _("Book"), "book", path);
      g_free (path);

      // letter
      path = g_strdup_printf (DATA_DIR "/templates/%s",
                  _("letter-en.tex"));
      add_template_from_file (default_store, _("Letter"), "letter", path);
      g_free (path);

      // presentation (beamer)
      path = g_strdup_printf (DATA_DIR "/templates/%s",
                  _("beamer-en.tex"));
      add_template_from_file (default_store, _("Presentation"), "beamer", path);
      g_free (path);

      /* personnal templates */
      personnal_store = gtk_list_store_new (N_COLS_TEMPLATE,
                  GDK_TYPE_PIXBUF, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING);
      nb_personnal_templates = 0;

      gchar *rc_file = get_rc_file ();
      if (! g_file_test (rc_file, G_FILE_TEST_EXISTS))
      {
            g_free (rc_file);
            return;
      }

      GKeyFile *key_file = g_key_file_new ();
      GError *error = NULL;
      g_key_file_load_from_file (key_file, rc_file, G_KEY_FILE_NONE, &error);
      g_free (rc_file);

      if (error != NULL)
      {
            print_warning ("load templates failed: %s", error->message);
            g_error_free (error);
            return;
      }

      gsize length;
      gchar **names = g_key_file_get_string_list (key_file, PROGRAM_NAME,
                  "names", &length, &error);
      if (error != NULL)
      {
            print_warning ("load templates failed: %s", error->message);
            g_error_free (error);
            g_key_file_free (key_file);
            return;
      }

      gchar **icons = g_key_file_get_string_list (key_file, PROGRAM_NAME,
                  "icons", NULL, &error);
      gboolean icons_ok = TRUE;
      if (error != NULL)
      {
            print_warning ("%s", error->message);
            g_error_free (error);
            icons_ok = FALSE;
      }

      nb_personnal_templates = length;

      gchar *rc_dir = get_rc_dir ();

      for (gint i = 0 ; i < length ; i++)
      {
            gchar *file = g_strdup_printf ("%s/%d.tex", rc_dir, i);
            if (! g_file_test (file, G_FILE_TEST_EXISTS))
            {
                  g_free (file);
                  continue;
            }

            add_template_from_file (personnal_store, names[i],
                        icons_ok ? icons[i] : "article", file);
            g_free (file);
      }

      g_strfreev (names);
      g_strfreev (icons);
      g_key_file_free (key_file);
      g_free (rc_dir);
}

static void
add_template_from_string (GtkListStore *store, const gchar *name,
            const gchar *icon_id, const gchar *contents)
{
      if (contents == NULL)
            return;

      GError *error = NULL;
      gchar *icon_path = g_strdup_printf (DATA_DIR "/images/templates/%s.png",
                  icon_id);
      GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file (icon_path, &error);
      g_free (icon_path);
      if (error != NULL)
      {
            print_warning ("impossible to load the icon of the template: %s",
                        error->message);
            g_error_free (error);
            return;
      }

      GtkTreeIter iter;
      gtk_list_store_append (store, &iter);
      gtk_list_store_set (store, &iter,
                  COL_TEMPLATE_PIXBUF, pixbuf,
                  COL_TEMPLATE_ICON_ID, icon_id,
                  COL_TEMPLATE_NAME, name,
                  COL_TEMPLATE_CONTENTS, contents,
                  -1);

      g_object_unref (pixbuf);
}

static void
add_template_from_file (GtkListStore *store, const gchar *name,
            const gchar *icon_id, const gchar *path)
{
      gchar *contents = NULL;
      GError *error = NULL;
      g_file_get_contents (path, &contents, NULL, &error);

      if (error != NULL)
      {
            print_warning ("impossible to load the template \"%s\": %s", name,
                        error->message);
            g_error_free (error);
            return;
      }

      gchar *text_utf8 = g_locale_to_utf8 (contents, -1, NULL, NULL, NULL);
      add_template_from_string (store, name, icon_id, text_utf8);

      g_free (text_utf8);
      g_free (contents);
}

static GtkWidget *
create_icon_view (GtkListStore *store)
{
      GtkWidget *icon_view = gtk_icon_view_new_with_model (
                  GTK_TREE_MODEL (store));
      gtk_icon_view_set_selection_mode (GTK_ICON_VIEW (icon_view),
                  GTK_SELECTION_SINGLE);
      gtk_icon_view_set_text_column (GTK_ICON_VIEW (icon_view),
                  COL_TEMPLATE_NAME);
      gtk_icon_view_set_pixbuf_column (GTK_ICON_VIEW (icon_view),
                  COL_TEMPLATE_PIXBUF);

      return icon_view;
}

static void
cb_icon_view_selection_changed (GtkIconView *icon_view,
            gpointer other_icon_view)
{
      // only one item of the two icon views can be selected at once
      
      // we unselect all the items of the other icon view only if the current icon
      // view have an item selected, because when we unselect all the items the
      // "selection-changed" signal is emitted for the other icon view, so for the
      // other icon view this function is also called but no item is selected so
      // nothing is done and the item selected by the user keeps selected

      GList *selected_items = gtk_icon_view_get_selected_items (icon_view);

      if (g_list_length (selected_items) > 0)
            gtk_icon_view_unselect_all ((GtkIconView *) other_icon_view);

      // free the GList
      g_list_foreach (selected_items, (GFunc) gtk_tree_path_free, NULL);
      g_list_free (selected_items);
}

static gchar *
get_rc_file (void)
{
      // rc_file must be freed
      gchar *rc_file = g_build_filename (g_get_user_data_dir (), "latexila",
                  "templatesrc", NULL);
      return rc_file;
}

static gchar *
get_rc_dir (void)
{
      // rc_dir must be freed
      gchar *rc_dir = g_build_filename (g_get_user_data_dir (), "latexila", NULL);
      return rc_dir;
}

static void
add_personnal_template (const gchar *contents)
{
      save_rc_file ();

      gchar *rc_dir = get_rc_dir ();

      gchar *file = g_strdup_printf ("%s/%d.tex", rc_dir,
                  nb_personnal_templates - 1);

      GError *error = NULL;
      g_file_set_contents (file, contents, -1, &error);

      if (error != NULL)
      {
            print_warning ("impossible to save templates: %s", error->message);
            g_error_free (error);
      }

      g_free (rc_dir);
      g_free (file);
}

static void
save_rc_file (void)
{
      if (nb_personnal_templates == 0)
      {
            gchar *rc_file = get_rc_file ();
            g_remove (rc_file);
            g_free (rc_file);
            return;
      }

      // the names of all the personnal templates
      gchar **names = g_malloc ((nb_personnal_templates + 1) * sizeof (gchar *));
      gchar **names_i = names;

      // the icons id of all the personnal templates
      gchar **icons = g_malloc ((nb_personnal_templates + 1) * sizeof (gchar *));
      gchar **icons_i = icons;
      
      // traverse the list store
      GtkTreeIter iter;
      GtkTreeModel *model = GTK_TREE_MODEL (personnal_store);
      gboolean valid_iter = gtk_tree_model_get_iter_first (model, &iter);
      while (valid_iter)
      {
            gtk_tree_model_get (model, &iter,
                        COL_TEMPLATE_NAME, names_i,
                        COL_TEMPLATE_ICON_ID, icons_i,
                        -1);
            valid_iter = gtk_tree_model_iter_next (model, &iter);
            names_i++;
            icons_i++;
      }

      // the last elements are NULL so we can use g_strfreev()
      *names_i = NULL;
      *icons_i = NULL;

      GKeyFile *key_file = g_key_file_new ();
      g_key_file_set_string_list (key_file, PROGRAM_NAME, "names",
                  (const gchar * const *) names, nb_personnal_templates);
      g_key_file_set_string_list (key_file, PROGRAM_NAME, "icons",
                  (const gchar * const *) icons, nb_personnal_templates);

      /* save the rc file */
      gchar *rc_file = get_rc_file ();
      gchar *rc_dir = get_rc_dir ();
      g_mkdir_with_parents(rc_dir, S_IRWXU);
      gchar *key_file_data = g_key_file_to_data (key_file, NULL, NULL);

      GError *error = NULL;
      g_file_set_contents (rc_file, key_file_data, -1, &error);

      if (error != NULL)
      {
            print_warning ("impossible to save templates: %s", error->message);
            g_error_free (error);
      }

      g_strfreev (names);
      g_strfreev (icons);
      g_free (rc_file);
      g_free (rc_dir);
      g_free (key_file_data);
      g_key_file_free (key_file);
}

/* save the contents of the personnal templates
 * the first personnal template is saved in 0.tex, the second in 1.tex, etc */
static void
save_contents (void)
{
      gchar *rc_dir = get_rc_dir ();

      // delete all the *.tex files
      gchar *command = g_strdup_printf ("rm -f %s/*.tex", rc_dir);
      system (command);
      g_free (command);

      // traverse the list store
      GtkTreeIter iter;
      GtkTreeModel *model = GTK_TREE_MODEL (personnal_store);
      gboolean valid_iter = gtk_tree_model_get_iter_first (model, &iter);
      gint i = 0;
      while (valid_iter)
      {
            gchar *contents;
            gtk_tree_model_get (model, &iter, COL_TEMPLATE_CONTENTS, &contents, -1);

            gchar *file = g_strdup_printf ("%s/%d.tex", rc_dir, i);

            GError *error = NULL;
            g_file_set_contents (file, contents, -1, &error);

            if (error != NULL)
            {
                  print_warning ("impossible to save the template: %s", error->message);
                  g_error_free (error);
                  error = NULL;
            }

            g_free (contents);
            g_free (file);

            valid_iter = gtk_tree_model_iter_next (model, &iter);
            i++;
      }

      g_free (rc_dir);
}

Generated by  Doxygen 1.6.0   Back to index