/*
 * gedit-multi-notebook.c
 * This file is part of gedit
 *
 * Copyright (C) 2010 - Ignacio Casal Quinteiro
 *
 * gedit 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.
 *
 * gedit 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 gedit; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor,
 * Boston, MA  02110-1301  USA
 */

#include "gedit-multi-notebook.h"
#include <tepl/tepl.h>
#include "gedit-enum-types-private.h"
#include "gedit-settings.h"
#include "gedit-tab-private.h"
#include "gedit-tab.h"

struct _GeditMultiNotebookPrivate
{
	GtkWidget *active_notebook;
	GList     *notebooks;
	gint       total_tabs;

	GeditTab  *active_tab;

	GeditNotebookShowTabsModeType show_tabs_mode;
	GSettings *ui_settings;
	TeplSettingsBindingGroup *settings_binding_group;

	guint      show_tabs : 1;
	guint      removing_notebook : 1;
};

enum
{
	PROP_0,
	PROP_ACTIVE_NOTEBOOK,
	PROP_ACTIVE_TAB,
	PROP_SHOW_TABS_MODE,
	LAST_PROP
};

static GParamSpec *properties[LAST_PROP];

enum
{
	NOTEBOOK_ADDED,
	NOTEBOOK_REMOVED,
	TAB_ADDED,
	TAB_REMOVED,
	SWITCH_TAB,
	TAB_CLOSE_REQUEST,
	CREATE_WINDOW,
	PAGE_REORDERED,
	SHOW_POPUP_MENU,
	LAST_SIGNAL
};

static guint signals[LAST_SIGNAL];

G_DEFINE_TYPE_WITH_PRIVATE (GeditMultiNotebook, gedit_multi_notebook, GTK_TYPE_GRID)

static void	remove_notebook		(GeditMultiNotebook *mnb,
					 GtkWidget          *notebook);

static void	update_tabs_visibility	(GeditMultiNotebook *mnb);

static void
gedit_multi_notebook_get_property (GObject    *object,
				   guint       prop_id,
				   GValue     *value,
				   GParamSpec *pspec)
{
	GeditMultiNotebook *mnb = GEDIT_MULTI_NOTEBOOK (object);

	switch (prop_id)
	{
		case PROP_ACTIVE_NOTEBOOK:
			g_value_set_object (value,
					    mnb->priv->active_notebook);
			break;
		case PROP_ACTIVE_TAB:
			g_value_set_object (value,
					    mnb->priv->active_tab);
			break;
		case PROP_SHOW_TABS_MODE:
			g_value_set_enum (value,
					  mnb->priv->show_tabs_mode);
			break;
		default:
			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
			break;
	}
}

static void
gedit_multi_notebook_set_property (GObject      *object,
				   guint         prop_id,
				   const GValue *value,
				   GParamSpec   *pspec)
{
	GeditMultiNotebook *mnb = GEDIT_MULTI_NOTEBOOK (object);

	switch (prop_id)
	{
		case PROP_SHOW_TABS_MODE:
			mnb->priv->show_tabs_mode = g_value_get_enum (value);
			update_tabs_visibility (mnb);
			break;
		default:
			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
			break;
	}
}

static void
gedit_multi_notebook_dispose (GObject *object)
{
	GeditMultiNotebook *mnb = GEDIT_MULTI_NOTEBOOK (object);

	g_clear_object (&mnb->priv->ui_settings);

	if (mnb->priv->settings_binding_group != NULL)
	{
		tepl_settings_binding_group_unbind (mnb->priv->settings_binding_group, object);
		tepl_settings_binding_group_free (mnb->priv->settings_binding_group);
		mnb->priv->settings_binding_group = NULL;
	}

	G_OBJECT_CLASS (gedit_multi_notebook_parent_class)->dispose (object);
}

static void
gedit_multi_notebook_finalize (GObject *object)
{
	GeditMultiNotebook *mnb = GEDIT_MULTI_NOTEBOOK (object);

	g_list_free (mnb->priv->notebooks);

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

static void
gedit_multi_notebook_class_init (GeditMultiNotebookClass *klass)
{
	GObjectClass *object_class = G_OBJECT_CLASS (klass);

	object_class->dispose = gedit_multi_notebook_dispose;
	object_class->finalize = gedit_multi_notebook_finalize;
	object_class->get_property = gedit_multi_notebook_get_property;
	object_class->set_property = gedit_multi_notebook_set_property;

	properties[PROP_ACTIVE_NOTEBOOK] =
		g_param_spec_object ("active-notebook",
		                     "Active Notebook",
		                     "The Active Notebook",
		                     GEDIT_TYPE_NOTEBOOK,
		                     G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
	properties[PROP_ACTIVE_TAB] =
		g_param_spec_object ("active-tab",
		                     "Active Tab",
		                     "The Active Tab",
		                     GEDIT_TYPE_TAB,
		                     G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
	properties[PROP_SHOW_TABS_MODE] =
		g_param_spec_enum ("show-tabs-mode",
		                   "Show Tabs Mode",
		                   "When tabs should be shown",
		                   GEDIT_TYPE_NOTEBOOK_SHOW_TABS_MODE_TYPE,
		                   GEDIT_NOTEBOOK_SHOW_TABS_ALWAYS,
		                   G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);

	g_object_class_install_properties (object_class, LAST_PROP, properties);

	signals[NOTEBOOK_ADDED] =
		g_signal_new ("notebook-added",
			      G_OBJECT_CLASS_TYPE (object_class),
			      G_SIGNAL_RUN_FIRST,
			      G_STRUCT_OFFSET (GeditMultiNotebookClass, notebook_added),
			      NULL, NULL, NULL,
			      G_TYPE_NONE,
			      1,
			      GEDIT_TYPE_NOTEBOOK);
	signals[NOTEBOOK_REMOVED] =
		g_signal_new ("notebook-removed",
			      G_OBJECT_CLASS_TYPE (object_class),
			      G_SIGNAL_RUN_FIRST,
			      G_STRUCT_OFFSET (GeditMultiNotebookClass, notebook_removed),
			      NULL, NULL, NULL,
			      G_TYPE_NONE,
			      1,
			      GEDIT_TYPE_NOTEBOOK);
	signals[TAB_ADDED] =
		g_signal_new ("tab-added",
			      G_OBJECT_CLASS_TYPE (object_class),
			      G_SIGNAL_RUN_FIRST,
			      G_STRUCT_OFFSET (GeditMultiNotebookClass, tab_added),
			      NULL, NULL, NULL,
			      G_TYPE_NONE,
			      2,
			      GEDIT_TYPE_NOTEBOOK,
			      GEDIT_TYPE_TAB);
	signals[TAB_REMOVED] =
		g_signal_new ("tab-removed",
			      G_OBJECT_CLASS_TYPE (object_class),
			      G_SIGNAL_RUN_FIRST,
			      G_STRUCT_OFFSET (GeditMultiNotebookClass, tab_removed),
			      NULL, NULL, NULL,
			      G_TYPE_NONE,
			      2,
			      GEDIT_TYPE_NOTEBOOK,
			      GEDIT_TYPE_TAB);
	signals[SWITCH_TAB] =
		g_signal_new ("switch-tab",
			      G_OBJECT_CLASS_TYPE (object_class),
			      G_SIGNAL_RUN_FIRST,
			      G_STRUCT_OFFSET (GeditMultiNotebookClass, switch_tab),
			      NULL, NULL, NULL,
			      G_TYPE_NONE,
			      4,
			      GEDIT_TYPE_NOTEBOOK,
			      GEDIT_TYPE_TAB,
			      GEDIT_TYPE_NOTEBOOK,
			      GEDIT_TYPE_TAB);
	signals[TAB_CLOSE_REQUEST] =
		g_signal_new ("tab-close-request",
			      G_OBJECT_CLASS_TYPE (object_class),
			      G_SIGNAL_RUN_FIRST,
			      G_STRUCT_OFFSET (GeditMultiNotebookClass, tab_close_request),
			      NULL, NULL, NULL,
			      G_TYPE_NONE,
			      2,
			      GEDIT_TYPE_NOTEBOOK,
			      GEDIT_TYPE_TAB);
	signals[CREATE_WINDOW] =
		g_signal_new ("create-window",
		              G_TYPE_FROM_CLASS (object_class),
		              G_SIGNAL_RUN_LAST,
		              G_STRUCT_OFFSET (GeditMultiNotebookClass, create_window),
		              NULL, NULL, NULL,
		              GTK_TYPE_NOTEBOOK, 4,
		              GEDIT_TYPE_NOTEBOOK, GTK_TYPE_WIDGET,
		              G_TYPE_INT, G_TYPE_INT);
	signals[PAGE_REORDERED] =
		g_signal_new ("page-reordered",
		              G_OBJECT_CLASS_TYPE (object_class),
		              G_SIGNAL_RUN_FIRST,
		              G_STRUCT_OFFSET (GeditMultiNotebookClass, page_reordered),
		              NULL, NULL, NULL,
		              G_TYPE_NONE,
		              3,
		              GEDIT_TYPE_NOTEBOOK, GTK_TYPE_WIDGET,
		              G_TYPE_INT);
	signals[SHOW_POPUP_MENU] =
		g_signal_new ("show-popup-menu",
			      G_OBJECT_CLASS_TYPE (object_class),
			      G_SIGNAL_RUN_FIRST,
			      G_STRUCT_OFFSET (GeditMultiNotebookClass, show_popup_menu),
			      NULL, NULL, NULL,
			      G_TYPE_NONE,
			      2,
			      GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE,
			      GEDIT_TYPE_TAB);
}

static void
notebook_show_popup_menu (GtkNotebook        *notebook,
                          GdkEvent           *event,
                          GeditTab           *tab,
                          GeditMultiNotebook *mnb)
{
	g_signal_emit (G_OBJECT (mnb), signals[SHOW_POPUP_MENU], 0, event, tab);
}

static void
notebook_tab_close_request (GeditNotebook      *notebook,
			    GeditTab           *tab,
			    GeditMultiNotebook *mnb)
{
	g_signal_emit (G_OBJECT (mnb), signals[TAB_CLOSE_REQUEST], 0,
		       notebook, tab);
}

static GtkNotebook *
notebook_create_window (GeditNotebook      *notebook,
			GtkWidget          *child,
			gint                x,
			gint                y,
			GeditMultiNotebook *mnb)
{
	GtkNotebook *dest_notebook;

	g_signal_emit (G_OBJECT (mnb), signals[CREATE_WINDOW], 0,
	               notebook, child, x, y, &dest_notebook);

	return dest_notebook;
}

static void
notebook_page_reordered (GeditNotebook      *notebook,
		         GtkWidget          *child,
		         guint               page_num,
		         GeditMultiNotebook *mnb)
{
	g_signal_emit (G_OBJECT (mnb), signals[PAGE_REORDERED], 0, notebook,
	               child, page_num);
}

static void
set_active_tab (GeditMultiNotebook *mnb,
                GeditTab           *tab)
{
	mnb->priv->active_tab = tab;
	g_object_notify_by_pspec (G_OBJECT (mnb), properties[PROP_ACTIVE_TAB]);
}

static void
notebook_page_removed (GtkNotebook        *notebook,
		       GtkWidget          *child,
		       guint               page_num,
		       GeditMultiNotebook *mnb)
{
	GeditTab *tab = GEDIT_TAB (child);
	guint num_tabs;
	gboolean last_notebook;

	--mnb->priv->total_tabs;
	num_tabs = gtk_notebook_get_n_pages (notebook);
	last_notebook = (mnb->priv->notebooks->next == NULL);

	if (mnb->priv->total_tabs == 0)
	{
		set_active_tab (mnb, NULL);
	}

	g_signal_emit (G_OBJECT (mnb), signals[TAB_REMOVED], 0, notebook, tab);

	/* Not last notebook but last tab of the notebook, this means we have
	   to remove the current notebook */
	if (num_tabs == 0 && !mnb->priv->removing_notebook &&
	    !last_notebook)
	{
		remove_notebook (mnb, GTK_WIDGET (notebook));
	}

	update_tabs_visibility (mnb);
}

static void
notebook_page_added (GtkNotebook        *notebook,
		     GtkWidget          *child,
		     guint               page_num,
		     GeditMultiNotebook *mnb)
{
	GeditTab *tab = GEDIT_TAB (child);

	++mnb->priv->total_tabs;

	update_tabs_visibility (mnb);

	g_signal_emit (G_OBJECT (mnb), signals[TAB_ADDED], 0, notebook, tab);
}

static void
notebook_switch_page (GtkNotebook        *book,
		      GtkWidget          *pg,
		      gint                page_num,
		      GeditMultiNotebook *mnb)
{
	GeditTab *tab;

	/* When we switch a tab from a notebook that it is not the active one
	   the switch page is emitted before the set focus, so we do this check
	   and we avoid to call switch page twice */
	if (GTK_WIDGET (book) != mnb->priv->active_notebook)
		return;

	/* CHECK: I don't know why but it seems notebook_switch_page is called
	two times every time the user change the active tab */
	tab = GEDIT_TAB (gtk_notebook_get_nth_page (book, page_num));
	if (tab != mnb->priv->active_tab)
	{
		GeditTab *old_tab;

		old_tab = mnb->priv->active_tab;
		set_active_tab (mnb, tab);
		g_signal_emit (G_OBJECT (mnb), signals[SWITCH_TAB], 0,
			       mnb->priv->active_notebook, old_tab,
			       book, tab);
	}
}

/* We need to figure out if the any of the internal widget of the notebook
   has got the focus to set the active notebook */
static void
notebook_set_focus (GtkContainer       *container,
		    GtkWidget          *widget,
		    GeditMultiNotebook *mnb)
{
	if (GEDIT_IS_NOTEBOOK (container) &&
	    GTK_WIDGET (container) != mnb->priv->active_notebook)
	{
		gint page_num;

		mnb->priv->active_notebook = GTK_WIDGET (container);

		page_num = gtk_notebook_get_current_page (GTK_NOTEBOOK (container));
		notebook_switch_page (GTK_NOTEBOOK (container), NULL,
				      page_num, mnb);

		g_object_notify_by_pspec (G_OBJECT (mnb), properties[PROP_ACTIVE_NOTEBOOK]);
	}
}

static void
show_tabs_changed (GObject     *object,
		   GParamSpec  *pspec,
		   gpointer    *data)
{
	update_tabs_visibility (GEDIT_MULTI_NOTEBOOK (data));
}

static void
update_tabs_visibility (GeditMultiNotebook *mnb)
{
	gboolean show_tabs;
	GList *l;

	if (mnb->priv->notebooks == NULL)
		return;

	if (!mnb->priv->show_tabs)
	{
		show_tabs = FALSE;
	}
	else if (mnb->priv->notebooks->next == NULL) /* only one notebook */
	{
		switch (mnb->priv->show_tabs_mode)
		{
			case GEDIT_NOTEBOOK_SHOW_TABS_NEVER:
				show_tabs = FALSE;
				break;
			case GEDIT_NOTEBOOK_SHOW_TABS_AUTO:
				show_tabs = gtk_notebook_get_n_pages (GTK_NOTEBOOK (mnb->priv->notebooks->data)) > 1;
				break;
			case GEDIT_NOTEBOOK_SHOW_TABS_ALWAYS:
			default:
				show_tabs = TRUE;
				break;
		}
	}
	else
	{
		show_tabs = (mnb->priv->show_tabs_mode != GEDIT_NOTEBOOK_SHOW_TABS_NEVER);
	}

	g_signal_handlers_block_by_func (mnb, show_tabs_changed, NULL);

	for (l = mnb->priv->notebooks; l != NULL; l = l->next)
	{
		gtk_notebook_set_show_tabs (GTK_NOTEBOOK (l->data), show_tabs);
	}

	g_signal_handlers_unblock_by_func (mnb, show_tabs_changed, NULL);
}

static void
connect_notebook_signals (GeditMultiNotebook *mnb,
			  GtkWidget          *notebook)
{
	g_signal_connect (notebook,
			  "set-focus-child",
			  G_CALLBACK (notebook_set_focus),
			  mnb);
	g_signal_connect (notebook,
			  "page-added",
			  G_CALLBACK (notebook_page_added),
			  mnb);
	g_signal_connect (notebook,
			  "page-removed",
			  G_CALLBACK (notebook_page_removed),
			  mnb);
	g_signal_connect (notebook,
			  "switch-page",
			  G_CALLBACK (notebook_switch_page),
			  mnb);
	g_signal_connect (notebook,
			  "page-reordered",
			  G_CALLBACK (notebook_page_reordered),
			  mnb);
	g_signal_connect (notebook,
			  "create-window",
			  G_CALLBACK (notebook_create_window),
			  mnb);
	g_signal_connect (notebook,
			  "tab-close-request",
			  G_CALLBACK (notebook_tab_close_request),
			  mnb);
	g_signal_connect (notebook,
			  "show-popup-menu",
			  G_CALLBACK (notebook_show_popup_menu),
			  mnb);
	g_signal_connect (notebook,
			  "notify::show-tabs",
			  G_CALLBACK (show_tabs_changed),
			  mnb);
}

static void
disconnect_notebook_signals (GeditMultiNotebook *mnb,
			     GtkWidget          *notebook)
{
	g_signal_handlers_disconnect_by_func (notebook, notebook_set_focus,
					      mnb);
	g_signal_handlers_disconnect_by_func (notebook, notebook_switch_page,
					      mnb);
	g_signal_handlers_disconnect_by_func (notebook, notebook_page_added,
					      mnb);
	g_signal_handlers_disconnect_by_func (notebook, notebook_page_removed,
					      mnb);
	g_signal_handlers_disconnect_by_func (notebook, notebook_page_reordered,
					      mnb);
	g_signal_handlers_disconnect_by_func (notebook, notebook_create_window,
					      mnb);
	g_signal_handlers_disconnect_by_func (notebook, notebook_tab_close_request,
					      mnb);
	g_signal_handlers_disconnect_by_func (notebook, notebook_show_popup_menu,
					      mnb);
	g_signal_handlers_disconnect_by_func (notebook, show_tabs_changed,
					      mnb);
}

static void
add_notebook (GeditMultiNotebook *mnb,
	      GtkWidget          *notebook,
	      gboolean            main_container)
{
	gtk_widget_set_hexpand (notebook, TRUE);
	gtk_widget_set_vexpand (notebook, TRUE);

	if (main_container)
	{
		gtk_container_add (GTK_CONTAINER (mnb), notebook);

		mnb->priv->notebooks = g_list_append (mnb->priv->notebooks,
		                                      notebook);
	}
	else
	{
		GtkWidget *paned;
		GtkWidget *parent;
		GtkAllocation allocation;
		GtkWidget *active_notebook = mnb->priv->active_notebook;
		gint active_nb_pos;

		paned = gtk_paned_new (GTK_ORIENTATION_HORIZONTAL);
		gtk_widget_show (paned);

		/* First we remove the active container from its parent to make
		   this we add a ref to it*/
		g_object_ref (active_notebook);
		parent = gtk_widget_get_parent (active_notebook);
		gtk_widget_get_allocation (active_notebook, &allocation);

		gtk_container_remove (GTK_CONTAINER (parent), active_notebook);
		gtk_container_add (GTK_CONTAINER (parent), paned);

		gtk_paned_pack1 (GTK_PANED (paned), active_notebook, TRUE, FALSE);
		g_object_unref (active_notebook);

		gtk_paned_pack2 (GTK_PANED (paned), notebook, FALSE, FALSE);

		/* We need to set the new paned in the right place */
		gtk_paned_set_position (GTK_PANED (paned),
		                        allocation.width / 2);

		active_nb_pos = g_list_index (mnb->priv->notebooks,
		                              active_notebook);
		mnb->priv->notebooks = g_list_insert (mnb->priv->notebooks,
		                                      notebook,
		                                      active_nb_pos + 1);
	}

	gtk_widget_show (notebook);

	connect_notebook_signals (mnb, notebook);

	g_signal_emit (G_OBJECT (mnb), signals[NOTEBOOK_ADDED], 0, notebook);
}

static void
remove_notebook (GeditMultiNotebook *mnb,
		 GtkWidget          *notebook)
{
	GtkWidget *parent;
	GtkWidget *grandpa;
	GList *children;
	GtkWidget *new_notebook;
	GList *current;

	if (mnb->priv->notebooks->next == NULL)
	{
		g_warning ("You are trying to remove the main notebook");
		return;
	}

	current = g_list_find (mnb->priv->notebooks,
			       notebook);

	if (current->next != NULL)
	{
		new_notebook = GTK_WIDGET (current->next->data);
	}
	else
	{
		new_notebook = GTK_WIDGET (mnb->priv->notebooks->data);
	}

	parent = gtk_widget_get_parent (notebook);

	/* Now we destroy the widget, we get the children of parent and we destroy
	  parent too as the parent is an useless paned. Finally we add the child
	  into the grand parent */
	g_object_ref (notebook);
	mnb->priv->removing_notebook = TRUE;

	gtk_widget_destroy (notebook);

	mnb->priv->notebooks = g_list_remove (mnb->priv->notebooks,
					      notebook);

	mnb->priv->removing_notebook = FALSE;

	children = gtk_container_get_children (GTK_CONTAINER (parent));
	if (children->next != NULL)
	{
		g_warning ("The parent is not a paned");
		return;
	}
	grandpa = gtk_widget_get_parent (parent);

	g_object_ref (children->data);
	gtk_container_remove (GTK_CONTAINER (parent),
			      GTK_WIDGET (children->data));
	gtk_widget_destroy (parent);
	gtk_container_add (GTK_CONTAINER (grandpa),
			   GTK_WIDGET (children->data));
	g_object_unref (children->data);
	g_list_free (children);

	disconnect_notebook_signals (mnb, notebook);

	g_signal_emit (G_OBJECT (mnb), signals[NOTEBOOK_REMOVED], 0, notebook);
	g_object_unref (notebook);

	/* Let's make the active notebook grab the focus */
	gtk_widget_grab_focus (new_notebook);
}

static void
gedit_multi_notebook_init (GeditMultiNotebook *mnb)
{
	GeditMultiNotebookPrivate *priv;

	mnb->priv = gedit_multi_notebook_get_instance_private (mnb);
	priv = mnb->priv;

	priv->removing_notebook = FALSE;

	gtk_orientable_set_orientation (GTK_ORIENTABLE (mnb),
	                                GTK_ORIENTATION_VERTICAL);

	priv->show_tabs_mode = GEDIT_NOTEBOOK_SHOW_TABS_ALWAYS;
	priv->show_tabs = TRUE;

	priv->settings_binding_group = tepl_settings_binding_group_new ();
	priv->ui_settings = g_settings_new ("org.gnome.gedit.preferences.ui");

	g_settings_bind (priv->ui_settings, GEDIT_SETTINGS_SHOW_TABS_MODE,
			 mnb, "show-tabs-mode",
			 G_SETTINGS_BIND_GET | G_SETTINGS_BIND_SET);
	tepl_settings_binding_group_add (priv->settings_binding_group, "show-tabs-mode");

	priv->active_notebook = gedit_notebook_new ();
	add_notebook (mnb, priv->active_notebook, TRUE);
}

GeditMultiNotebook *
gedit_multi_notebook_new (void)
{
	return g_object_new (GEDIT_TYPE_MULTI_NOTEBOOK, NULL);
}

GeditNotebook *
gedit_multi_notebook_get_active_notebook (GeditMultiNotebook *mnb)
{
	g_return_val_if_fail (GEDIT_IS_MULTI_NOTEBOOK (mnb), NULL);

	return GEDIT_NOTEBOOK (mnb->priv->active_notebook);
}

gint
gedit_multi_notebook_get_n_notebooks (GeditMultiNotebook *mnb)
{
	g_return_val_if_fail (GEDIT_IS_MULTI_NOTEBOOK (mnb), 0);

	return g_list_length (mnb->priv->notebooks);
}

GeditNotebook *
gedit_multi_notebook_get_nth_notebook (GeditMultiNotebook *mnb,
				       gint                notebook_num)
{
	g_return_val_if_fail (GEDIT_IS_MULTI_NOTEBOOK (mnb), NULL);

	return g_list_nth_data (mnb->priv->notebooks, notebook_num);
}

GeditNotebook *
gedit_multi_notebook_get_notebook_for_tab (GeditMultiNotebook *mnb,
                                           GeditTab           *tab)
{
	GList *l;
	gint page_num;

	g_return_val_if_fail (GEDIT_IS_MULTI_NOTEBOOK (mnb), NULL);
	g_return_val_if_fail (GEDIT_IS_TAB (tab), NULL);

	l = mnb->priv->notebooks;

	do
	{
		page_num = gtk_notebook_page_num (GTK_NOTEBOOK (l->data),
		                                  GTK_WIDGET (tab));
		if (page_num != -1)
			break;

		l = g_list_next (l);
	} while (l != NULL && page_num == -1);

	g_return_val_if_fail (page_num != -1, NULL);

	return GEDIT_NOTEBOOK (l->data);
}

gint
gedit_multi_notebook_get_notebook_num (GeditMultiNotebook *mnb,
				       GeditNotebook      *notebook)
{
	g_return_val_if_fail (GEDIT_IS_MULTI_NOTEBOOK (mnb), -1);
	g_return_val_if_fail (GEDIT_IS_NOTEBOOK (notebook), -1);

	return g_list_index (mnb->priv->notebooks, notebook);
}

gint
gedit_multi_notebook_get_n_tabs (GeditMultiNotebook *mnb)
{
	g_return_val_if_fail (GEDIT_IS_MULTI_NOTEBOOK (mnb), 0);

	return mnb->priv->total_tabs;
}

gint
gedit_multi_notebook_get_page_num (GeditMultiNotebook *mnb,
				   GeditTab           *tab)
{
	GList *l;
	gint real_page_num = 0;

	for (l = mnb->priv->notebooks; l != NULL; l = g_list_next (l))
	{
		gint page_num;

		page_num = gtk_notebook_page_num (GTK_NOTEBOOK (l->data),
						  GTK_WIDGET (tab));

		if (page_num != -1)
		{
			real_page_num += page_num;
			break;
		}

		real_page_num += gtk_notebook_get_n_pages (GTK_NOTEBOOK (l->data));
	}

	return real_page_num;
}

GeditTab *
gedit_multi_notebook_get_active_tab (GeditMultiNotebook *mnb)
{
	g_return_val_if_fail (GEDIT_IS_MULTI_NOTEBOOK (mnb), NULL);

	return (mnb->priv->active_tab == NULL) ?
				NULL : GEDIT_TAB (mnb->priv->active_tab);
}

void
gedit_multi_notebook_set_active_tab (GeditMultiNotebook *mnb,
				     GeditTab           *tab)
{
	GList *l;
	gint page_num;

	g_return_if_fail (GEDIT_IS_MULTI_NOTEBOOK (mnb));
	g_return_if_fail (GEDIT_IS_TAB (tab) || tab == NULL);

	/* use plain C cast since the active tab can be null */
	if (tab == (GeditTab *) mnb->priv->active_tab)
	{
		return;
	}

	if (tab == NULL)
	{
		set_active_tab (mnb, NULL);
		return;
	}

	l = mnb->priv->notebooks;

	do
	{
		page_num = gtk_notebook_page_num (GTK_NOTEBOOK (l->data),
		                                  GTK_WIDGET (tab));
		if (page_num != -1)
			break;

		l = g_list_next (l);
	} while (l != NULL && page_num == -1);

	g_return_if_fail (page_num != -1);

	gtk_notebook_set_current_page (GTK_NOTEBOOK (l->data), page_num);

	if (GTK_WIDGET (l->data) != mnb->priv->active_notebook)
	{
		gtk_widget_grab_focus (GTK_WIDGET (l->data));
	}
}

void
gedit_multi_notebook_set_current_page (GeditMultiNotebook *mnb,
				       gint                page_num)
{
	GList *l;
	gint pages = 0;
	gint single_num = page_num;

	g_return_if_fail (GEDIT_IS_MULTI_NOTEBOOK (mnb));

	for (l = mnb->priv->notebooks; l != NULL; l = g_list_next (l))
	{
		gint p;

		p = gtk_notebook_get_n_pages (GTK_NOTEBOOK (l->data));
		pages += p;

		if ((pages - 1) >= page_num)
			break;

		single_num -= p;
	}

	if (l == NULL)
		return;

	if (GTK_WIDGET (l->data) != mnb->priv->active_notebook)
	{
		gtk_widget_grab_focus (GTK_WIDGET (l->data));
	}

	gtk_notebook_set_current_page (GTK_NOTEBOOK (l->data), single_num);
}

GList *
gedit_multi_notebook_get_all_tabs (GeditMultiNotebook *mnb)
{
	GList *nbs;
	GList *ret = NULL;

	g_return_val_if_fail (GEDIT_IS_MULTI_NOTEBOOK (mnb), NULL);

	for (nbs = mnb->priv->notebooks; nbs != NULL; nbs = g_list_next (nbs))
	{
		GList *l, *children;

		children = gtk_container_get_children (GTK_CONTAINER (nbs->data));

		for (l = children; l != NULL; l = g_list_next (l))
		{
			ret = g_list_prepend (ret, l->data);
		}

		g_list_free (children);
	}

	ret = g_list_reverse (ret);

	return ret;
}

void
gedit_multi_notebook_close_tabs (GeditMultiNotebook *mnb,
				 const GList        *tabs)
{
	GList *l;

	g_return_if_fail (GEDIT_IS_MULTI_NOTEBOOK (mnb));

	for (l = (GList *)tabs; l != NULL; l = g_list_next (l))
	{
		GList *nbs;

		for (nbs = mnb->priv->notebooks; nbs != NULL; nbs = g_list_next (nbs))
		{
			gint n;

			n = gtk_notebook_page_num (GTK_NOTEBOOK (nbs->data),
			                           GTK_WIDGET (l->data));

			if (n != -1)
			{
				gtk_container_remove (GTK_CONTAINER (nbs->data),
				                      GTK_WIDGET (l->data));
				break;
			}
		}
	}
}

/**
 * gedit_multi_notebook_close_all_tabs:
 * @mnb: a #GeditMultiNotebook
 *
 * Closes all opened tabs.
 */
void
gedit_multi_notebook_close_all_tabs (GeditMultiNotebook *mnb)
{
	GList *nbs, *l;

	g_return_if_fail (GEDIT_MULTI_NOTEBOOK (mnb));

	/* We copy the list because the main one is going to have the items
	   removed */
	nbs = g_list_copy (mnb->priv->notebooks);

	for (l = nbs; l != NULL; l = g_list_next (l))
	{
		gedit_notebook_remove_all_tabs (GEDIT_NOTEBOOK (l->data));
	}

	g_list_free (nbs);
}

void
gedit_multi_notebook_add_new_notebook (GeditMultiNotebook *mnb)
{
	GtkWidget *notebook;
	GeditTab *tab;

	g_return_if_fail (GEDIT_IS_MULTI_NOTEBOOK (mnb));

	notebook = gedit_notebook_new ();
	add_notebook (mnb, notebook, FALSE);

	tab = _gedit_tab_new ();
	gtk_widget_show (GTK_WIDGET (tab));

	/* When gtk_notebook_insert_page is called the focus is set in
	   the notebook, we don't want this to happen until the page is added.
	   Also we don't want to call switch_page when we add the tab
	   but when we switch the notebook. */
	g_signal_handlers_block_by_func (notebook, notebook_set_focus, mnb);
	g_signal_handlers_block_by_func (notebook, notebook_switch_page, mnb);

	gedit_notebook_add_tab (GEDIT_NOTEBOOK (notebook),
				tab,
				-1,
				TRUE);

	g_signal_handlers_unblock_by_func (notebook, notebook_switch_page, mnb);
	g_signal_handlers_unblock_by_func (notebook, notebook_set_focus, mnb);

	notebook_set_focus (GTK_CONTAINER (notebook), NULL, mnb);
}

void
gedit_multi_notebook_add_new_notebook_with_tab (GeditMultiNotebook *mnb,
                                                GeditTab           *tab)
{
	GtkWidget *notebook;
	GeditNotebook *old_notebook;

	g_return_if_fail (GEDIT_IS_MULTI_NOTEBOOK (mnb));
	g_return_if_fail (GEDIT_IS_TAB (tab));

	notebook = gedit_notebook_new ();
	add_notebook (mnb, notebook, FALSE);

	old_notebook = gedit_multi_notebook_get_notebook_for_tab (mnb, tab);

	/* When gtk_notebook_insert_page is called the focus is set in
	   the notebook, we don't want this to happen until the page is added.
	   Also we don't want to call switch_page when we add the tab
	   but when we switch the notebook. */
	g_signal_handlers_block_by_func (old_notebook, notebook_set_focus, mnb);
	g_signal_handlers_block_by_func (old_notebook, notebook_switch_page, mnb);

	gedit_notebook_move_tab (old_notebook,
	                         GEDIT_NOTEBOOK (notebook),
	                         tab,
	                         -1);

	g_signal_handlers_unblock_by_func (old_notebook, notebook_switch_page, mnb);
	g_signal_handlers_unblock_by_func (old_notebook, notebook_set_focus, mnb);

	notebook_set_focus (GTK_CONTAINER (notebook), NULL, mnb);
}

void
gedit_multi_notebook_remove_active_notebook (GeditMultiNotebook *mnb)
{
	g_return_if_fail (GEDIT_IS_MULTI_NOTEBOOK (mnb));

	gedit_notebook_remove_all_tabs (GEDIT_NOTEBOOK (mnb->priv->active_notebook));
}

void
gedit_multi_notebook_previous_notebook (GeditMultiNotebook *mnb)
{
	GList *current;
	GtkWidget *notebook;

	g_return_if_fail (GEDIT_IS_MULTI_NOTEBOOK (mnb));

	current = g_list_find (mnb->priv->notebooks,
			       mnb->priv->active_notebook);

	if (current->prev != NULL)
		notebook = GTK_WIDGET (current->prev->data);
	else
		notebook = GTK_WIDGET (g_list_last (mnb->priv->notebooks)->data);

	gtk_widget_grab_focus (notebook);
}

void
gedit_multi_notebook_next_notebook (GeditMultiNotebook *mnb)
{
	GList *current;
	GtkWidget *notebook;

	g_return_if_fail (GEDIT_IS_MULTI_NOTEBOOK (mnb));

	current = g_list_find (mnb->priv->notebooks,
			       mnb->priv->active_notebook);

	if (current->next != NULL)
		notebook = GTK_WIDGET (current->next->data);
	else
		notebook = GTK_WIDGET (mnb->priv->notebooks->data);

	gtk_widget_grab_focus (notebook);
}

void
gedit_multi_notebook_foreach_notebook (GeditMultiNotebook *mnb,
				       GtkCallback         callback,
				       gpointer            callback_data)
{
	GList *l;

	g_return_if_fail (GEDIT_IS_MULTI_NOTEBOOK (mnb));

	for (l = mnb->priv->notebooks; l != NULL; l = g_list_next (l))
	{
		callback (GTK_WIDGET (l->data), callback_data);
	}
}

void
gedit_multi_notebook_foreach_tab (GeditMultiNotebook *mnb,
				  GtkCallback         callback,
				  gpointer            callback_data)
{
	GList *nb;

	g_return_if_fail (GEDIT_IS_MULTI_NOTEBOOK (mnb));

	for (nb = mnb->priv->notebooks; nb != NULL; nb = g_list_next (nb))
	{
		GList *l, *children;

		children = gtk_container_get_children (GTK_CONTAINER (nb->data));

		for (l = children; l != NULL; l = g_list_next (l))
		{
			callback (GTK_WIDGET (l->data), callback_data);
		}

		g_list_free (children);
	}
}

/* We only use this to hide tabs in fullscreen mode so for now
 * we do not have a real property etc.
 */
void
_gedit_multi_notebook_set_show_tabs (GeditMultiNotebook *mnb,
				     gboolean           show)
{
	g_return_if_fail (GEDIT_IS_MULTI_NOTEBOOK (mnb));

	mnb->priv->show_tabs = show != FALSE;

	update_tabs_visibility (mnb);
}

/* ex:set ts=8 noet: */
