/* NessusClient
 * Copyright (C) 2005 Renaud Deraison
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2
 * as published by the Free Software Foundation.
 *
 * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 * In addition, as a special exception, Renaud Deraison
 * gives permission to link the code of this program with any
 * version of the OpenSSL library which is distributed under a
 * license identical to that listed in the included COPYING.OpenSSL
 * file, and distribute linked combinations including the two.
 * You must obey the GNU General Public License in all respects
 * for all of the code used other than OpenSSL.  If you modify
 * this file, you may extend this exception to your version of the
 * file, but you are not obligated to do so.  If you do not wish to
 * do so, delete this exception statement from your version.
 */

/* This module defines a list notebook, that is a notebook whose pages
 * are controlled by a list box.  The gtk widget the represents such a
 * listnotebook is a paned window with a tree view showing the list of
 * pages and a notebook.
 */

#ifdef USE_GTK
#include <gtk/gtk.h>

#include "listnotebook.h"

enum {
  COL_PAGENAME,
  COL_PAGENUM,
  COL_ICON,
  NUM_COLS
};

/* selection func for the tree view.  Change the notebook page according
 * to the new selection in the tree view */
static gboolean
selection_func(selection, model, path, path_currently_selected, userdata)
  GtkTreeSelection *selection;
  GtkTreeModel *model;
  GtkTreePath *path;
  gboolean path_currently_selected;
  gpointer userdata;
{
  if (!path_currently_selected) {
    GtkTreeIter iter;
    GtkWidget *optionsnb = GTK_WIDGET(userdata);
    gint pagenr;

    gtk_tree_model_get_iter(model, &iter, path);
    gtk_tree_model_get(model, &iter, COL_PAGENUM, &pagenr, -1);

    gtk_notebook_set_current_page(GTK_NOTEBOOK(optionsnb), pagenr);
  }
  return TRUE; /* allow selection state to change */
}


/* Change the selection of the list to match the notebook's current page
 */
static void
notebook_page_switched(notebook, page, page_num, user_data)
  GtkNotebook *notebook;
  GtkNotebookPage *page;
  guint page_num;
  gpointer user_data;
{
  GtkTreeView *list = GTK_TREE_VIEW(user_data);
  GtkTreeModel *model = gtk_tree_view_get_model(list);
  GtkTreePath *store_path = gtk_tree_path_new();
  GtkTreePath *sorted_path = NULL;
  GtkTreePath *current_path = NULL;

  /* convert the page number to a path in the sorted model.  We can
   * derive a path pointing to the row in the list store directly from
   * the page number because the page number is the same as the index of
   * the row.  This assumes that all pages are added with
   * listnotebook_add_page. */
  gtk_tree_path_append_index(store_path, page_num);
  sorted_path = gtk_tree_model_sort_convert_child_path_to_path(
    GTK_TREE_MODEL_SORT(model), store_path);

  /* If the list is empty, sorted_path will be NULL because the
   * store_path we created above is invalid */
  if (sorted_path != NULL)
  {
    /* To avoid recursion we only change the selection if either no row is
     * currently selected or the current selection doesn't match the
     * current notebook page. */
    gtk_tree_view_get_cursor(list, &current_path, NULL);
    if (current_path == NULL ||
	gtk_tree_path_compare(sorted_path, current_path) != 0)
      gtk_tree_view_set_cursor(list, sorted_path, NULL, FALSE);
  }

  if (sorted_path != NULL)
    gtk_tree_path_free(sorted_path);
  if (current_path != NULL)
    gtk_tree_path_free(current_path);
}


/* Create the listnotebook.
 *
 * If horizontal is TRUE, use a horizontal paned window, otherwise a
 * vertical one.
 */

GtkWidget *
listnotebook_new(horizontal, sorted)
  gboolean horizontal;
  gboolean sorted;
{
  GtkWidget *paned;
  GtkWidget *notebook;
  GtkWidget *list;
  GtkWidget *scrolled;
  GtkListStore *store;
  GtkTreeModel *model;
  GtkTreeViewColumn *column;
  GtkCellRenderer *renderer;
  GtkTreeSelection *selection;

  if (horizontal)
    paned = gtk_hpaned_new();
  else
    paned = gtk_vpaned_new();

  /*
   * The list that will be used to select the notebook page
   */
  store = gtk_list_store_new(NUM_COLS, G_TYPE_STRING, G_TYPE_INT,
      GDK_TYPE_PIXBUF);

  /* wrap store in a sorted model and optionally sort it. */
  model = gtk_tree_model_sort_new_with_model(GTK_TREE_MODEL(store));
  if (sorted)
    gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(model),
	COL_PAGENAME, GTK_SORT_ASCENDING);

  scrolled = gtk_scrolled_window_new(NULL, NULL);
  /* in a horizontal listnotebook, we don't want horizontal scrollbars
   * in the list of pages.  If we allow horizontal scrollbars the
   * initial size is much too narrow */
  gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled),
      horizontal ? GTK_POLICY_NEVER : GTK_POLICY_AUTOMATIC,
      GTK_POLICY_AUTOMATIC);
  gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolled),
      GTK_SHADOW_IN);
  gtk_paned_add1(GTK_PANED(paned), scrolled);
  gtk_widget_show(scrolled);

  list = gtk_tree_view_new_with_model(model);
  gtk_widget_show(list);
  gtk_container_add(GTK_CONTAINER(scrolled), list);

  gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(list), FALSE);
  column = gtk_tree_view_column_new();
  gtk_tree_view_append_column(GTK_TREE_VIEW(list), column);
  renderer = gtk_cell_renderer_pixbuf_new();
  gtk_tree_view_column_pack_start(column, renderer, FALSE);
  gtk_tree_view_column_add_attribute(column, renderer, "pixbuf", COL_ICON);
  renderer = gtk_cell_renderer_text_new();
  gtk_tree_view_column_pack_start(column, renderer, TRUE);
  gtk_tree_view_column_add_attribute(column, renderer, "text", COL_PAGENAME);

  /*
   * Set up a notebook in the box
   */
  notebook = gtk_notebook_new();
  gtk_paned_add2(GTK_PANED(paned), notebook);
  gtk_notebook_set_show_tabs(GTK_NOTEBOOK(notebook), FALSE);
  gtk_notebook_set_show_border(GTK_NOTEBOOK(notebook), FALSE);
  gtk_widget_show(notebook);

  /* Hook up the list and notebook so that clicking into the list
   * changes the notebook page and that the selection in the list always
   * matches the notebook even if the notebook page is switched by other
   * means  */
  selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(list));
  gtk_tree_selection_set_mode(selection, GTK_SELECTION_BROWSE);
  gtk_tree_selection_set_select_function(selection, selection_func, notebook,
      NULL);
  g_signal_connect(G_OBJECT(notebook), "switch-page", notebook_page_switched,
      list);

  /* add the notebook, tree view and store as data to the box so that we
   * can easily access them later */
  g_object_set_data(G_OBJECT(paned), "notebook", notebook);
  g_object_set_data(G_OBJECT(paned), "treeview", list);
  g_object_set_data(G_OBJECT(paned), "liststore", store);

  return paned;
}



/* Add a page to the listnotebook in the box.
 *
 * The parameter page should be the widget that makes up the new page.
 * It will be added to the notebook and the list store.  The parameter
 * title is the title of the page used in the list.  The stock-id, if
 * not NULL, should be the name of the icon to use.
 */
void
listnotebook_add_page(listnotebook, page, title, stock_id)
  GtkWidget *listnotebook;
  GtkWidget *page;
  const char *title;
  const char *stock_id;
{
  GtkWidget *notebook = g_object_get_data(G_OBJECT(listnotebook), "notebook");
  GtkListStore *store = g_object_get_data(G_OBJECT(listnotebook), "liststore");
  int pagenum = gtk_tree_model_iter_n_children(GTK_TREE_MODEL(store), NULL);
  GtkTreeIter iter;
  GdkPixbuf *icon = NULL;

  if (stock_id != NULL)
    icon = gtk_widget_render_icon(notebook, stock_id,
	GTK_ICON_SIZE_LARGE_TOOLBAR, NULL);

  gtk_widget_show(page);
  gtk_notebook_append_page(GTK_NOTEBOOK(notebook), page, NULL);

  gtk_list_store_append(store, &iter);
  gtk_list_store_set(store, &iter, COL_ICON, icon,
      COL_PAGENAME, title, COL_PAGENUM, pagenum, -1);

  if (icon != NULL)
    g_object_unref(G_OBJECT(icon));
}


/* Select a specific notebook page. The page_num is the index in the
 * (optionallly sorted) list, so that 0 is the page listed first */
void
listnotebook_select_page(listnotebook, page_num)
  GtkWidget*listnotebook;
  int page_num;
{
  GtkTreeView *list =  g_object_get_data(G_OBJECT(listnotebook), "treeview");
  GtkTreeModel *model = gtk_tree_view_get_model(list);
  GtkTreeIter iter;

  if (gtk_tree_model_iter_nth_child(model, &iter, NULL, page_num))
  {
    GtkTreeSelection *selection = gtk_tree_view_get_selection(list);
    gtk_tree_selection_select_iter(selection, &iter);
  }
}

#endif /* USE_GTK */
