/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
/*
 * Copyright (C) 2001-2005 Imendio AB
 *
 * 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.
 */

#include <config.h>
#include <string.h>
#include <glib/gi18n.h>
#include <gdk/gdkkeysyms.h>
#include <gtk/gtk.h>
#include <gconf/gconf-client.h>

#include "dh-book-tree.h"
#include "dh-html.h"
#include "dh-preferences.h"
#include "dh-search.h"
#include "dh-window.h"
#include "eggfindbar.h"

struct _DhWindowPriv {
	DhBase         *base;

	GConfClient    *gconf_client;

	GtkWidget      *main_box;
	GtkWidget      *menu_box;
	GtkWidget      *hpaned;
        GtkWidget      *control_notebook;
        GtkWidget      *book_tree;
	GtkWidget      *search;
	GtkWidget      *html_notebook;

	GtkWidget      *vbox;
	GtkWidget      *findbar;

	GtkUIManager   *manager;
	GtkActionGroup *action_group;
};

static guint tab_accel_keys[] = {
	GDK_1, GDK_2, GDK_3, GDK_4, GDK_5,
	GDK_6, GDK_7, GDK_8, GDK_9, GDK_0
};

/* People have reported problems with the default values in GConf so I'm
 * adding this to make sure that the window isn't started 1x1 pixels or the
 * paned having size 0
 */
#define DEFAULT_WIDTH     700
#define DEFAULT_HEIGHT    500
#define DEFAULT_PANED_LOC 250

static void       window_class_init               (DhWindowClass   *klass);
static void       window_init                     (DhWindow        *window);
static void       window_finalize                 (GObject         *object);
static void       window_populate                 (DhWindow        *window);
static void       window_activate_new_window      (GtkAction       *action,
						   DhWindow        *window);
static void       window_activate_new_tab         (GtkAction       *action,
						   DhWindow        *window);
static void       window_activate_close           (GtkAction       *action,
						   DhWindow        *window);
static void       window_activate_quit            (GtkAction       *action,
						   DhWindow        *window);
static void       window_activate_copy            (GtkAction       *action,
						   DhWindow        *window);
static void       window_activate_find            (GtkAction       *action,
						   DhWindow        *window);
static void       window_activate_preferences     (GtkAction       *action,
						   DhWindow        *window);
static void       window_activate_back            (GtkAction       *action,
						   DhWindow        *window);
static void       window_activate_forward         (GtkAction       *action,
						   DhWindow        *window);
static void       window_activate_show_contents   (GtkAction       *action,
						   DhWindow        *window);
static void       window_activate_show_search     (GtkAction       *action,
						   DhWindow        *window);
static void       window_activate_about           (GtkAction       *action,
						   DhWindow        *window);
static void       window_save_state               (DhWindow        *window);
static void       window_restore_state            (DhWindow        *window);
static gboolean   window_delete_cb                (GtkWidget       *widget,
						   GdkEventAny     *event,
						   gpointer         user_data);
static void       window_tree_link_selected_cb    (GObject         *ignored,
						   DhLink          *link,
						   DhWindow        *window);
static void       window_search_link_selected_cb  (GObject         *ignored,
						   DhLink          *link,
						   DhWindow        *window);
static void       window_manager_add_widget       (GtkUIManager    *manager,
						   GtkWidget       *widget,
						   DhWindow        *window);
static void       window_check_history            (DhWindow        *window,
						   DhHtml          *html);
static void       window_html_location_changed_cb (DhHtml          *html,
						   const gchar     *location,
						   DhWindow        *window);
static void       window_html_title_changed_cb    (DhHtml          *html,
						   const gchar     *location,
						   DhWindow        *window);
static gboolean   window_html_open_uri_cb         (DhHtml          *html,
						   const gchar     *uri,
						   DhWindow        *window);
static void       window_html_open_new_tab_cb     (DhHtml          *html,
						   const gchar     *location,
						   DhWindow        *window);
static void       window_html_tab_accel_cb        (GtkAccelGroup   *accel_group,
						   GObject         *object,
						   guint            key,
						   GdkModifierType  mod,
						   DhWindow        *window);
static void       window_findbar_search_changed_cb(GObject         *object,
						   GParamSpec      *arg1,
						   DhWindow        *window);
static void       window_findbar_case_sensitive_changed_cb (GObject         *object,
							    GParamSpec      *arg1,
							    DhWindow        *window);
static void       window_find_previous_cb         (GtkEntry        *entry,
						   DhWindow        *window);
static void       window_find_next_cb             (GtkEntry        *entry,
						   DhWindow        *window);
static void       window_findbar_close_cb         (GtkWidget       *widget,
						   DhWindow        *window);
static GtkWidget *window_new_tab_label            (DhWindow        *window,
						   const gchar     *label);
static void       window_open_new_tab             (DhWindow        *window,
						   const gchar     *location);
static DhHtml *   window_get_active_html          (DhWindow        *window);
static void       window_update_title             (DhWindow        *window,
						   DhHtml          *html);
static void       window_tab_set_title            (DhWindow        *window,
						   DhHtml          *html,
						   const gchar     *title);


static GtkWindowClass *parent_class = NULL;

static const GtkActionEntry actions[] = {
	{ "FileMenu", NULL, N_("_File") },
	{ "EditMenu", NULL, N_("_Edit") },
	{ "GoMenu",   NULL, N_("_Go") },
	{ "HelpMenu", NULL, N_("_Help") },

	/* File menu */
	{ "NewWindow", GTK_STOCK_NEW, N_("_New Window"), "<control>N", NULL,
	  G_CALLBACK (window_activate_new_window) },
	{ "NewTab", GTK_STOCK_NEW, N_("New _Tab"), "<control>T", NULL,
	  G_CALLBACK (window_activate_new_tab) },
	{ "Close", GTK_STOCK_CLOSE, NULL, NULL, NULL,
	  G_CALLBACK (window_activate_close) },
	{ "Quit", GTK_STOCK_QUIT, NULL, NULL, NULL,
	  G_CALLBACK (window_activate_quit) },

	/* Edit menu */
	{ "Copy", GTK_STOCK_COPY, NULL, "<control>C", NULL,
	  G_CALLBACK (window_activate_copy) },
	{ "Find", GTK_STOCK_FIND, NULL, "<control>F", NULL,
	  G_CALLBACK (window_activate_find) },
	{ "Find Next", GTK_STOCK_GO_FORWARD, N_("Find Next"), "<control>G", NULL,
	  G_CALLBACK (window_find_next_cb) },
	{ "Find Previous", GTK_STOCK_GO_BACK, N_("Find Previous"), "<shift><control>G", NULL,
	  G_CALLBACK (window_find_previous_cb) },
	{ "Preferences", GTK_STOCK_PREFERENCES, NULL, NULL, NULL,
	  G_CALLBACK (window_activate_preferences) },

	/* Go menu */
	{ "Back", GTK_STOCK_GO_BACK, NULL, "<alt>Left", NULL,
	  G_CALLBACK (window_activate_back) },
	{ "Forward", GTK_STOCK_GO_FORWARD, NULL, "<alt>Right", NULL,
	  G_CALLBACK (window_activate_forward) },

	{ "ShowContentsTab", NULL, N_("_Contents Tab"), "<ctrl>B", NULL,
	  G_CALLBACK (window_activate_show_contents) },

	{ "ShowSearchTab", NULL, N_("_Search Tab"), "<ctrl>S", NULL,
	  G_CALLBACK (window_activate_show_search) },

	/* About menu */
	{ "About", GTK_STOCK_ABOUT, NULL, NULL, NULL,
	  G_CALLBACK (window_activate_about) },
};

GType
dh_window_get_type (void)
{
        static GType type = 0;

        if (!type) {
                static const GTypeInfo info = {
			sizeof (DhWindowClass),
			NULL,
			NULL,
			(GClassInitFunc) window_class_init,
			NULL,
			NULL,
			sizeof (DhWindow),
			0,
			(GInstanceInitFunc) window_init
		};

                type = g_type_register_static (GTK_TYPE_WINDOW,
					       "DhWindow",
					       &info, 0);
        }

        return type;
}

static void
window_class_init (DhWindowClass *klass)
{
        GObjectClass *object_class;

        parent_class = g_type_class_peek_parent (klass);

        object_class = G_OBJECT_CLASS (klass);

        object_class->finalize = window_finalize;
}

static void
window_init (DhWindow *window)
{
	DhWindowPriv  *priv;
	GtkAction     *action;
	GtkAccelGroup *accel_group;
	GClosure      *closure;
	gint           i;

	priv = g_new0 (DhWindowPriv, 1);
        window->priv = priv;

	priv->manager = gtk_ui_manager_new ();

	priv->gconf_client = gconf_client_get_default ();

	accel_group = gtk_ui_manager_get_accel_group (priv->manager);
	gtk_window_add_accel_group (GTK_WINDOW (window), accel_group);

	priv->main_box = gtk_vbox_new (FALSE, 0);
	gtk_widget_show (priv->main_box);

	priv->menu_box = gtk_vbox_new (FALSE, 0);
	gtk_widget_show (priv->menu_box);
	gtk_container_set_border_width (GTK_CONTAINER (priv->menu_box), 0);
	gtk_box_pack_start (GTK_BOX (priv->main_box), priv->menu_box,
			    FALSE, TRUE, 0);

	gtk_container_add (GTK_CONTAINER (window), priv->main_box);

	g_signal_connect (priv->manager,
			  "add-widget",
			  G_CALLBACK (window_manager_add_widget),
			  window);

	priv->action_group = gtk_action_group_new ("MainWindow");

	gtk_action_group_set_translation_domain (priv->action_group,
						 GETTEXT_PACKAGE);

	gtk_action_group_add_actions (priv->action_group,
				      actions,
				      G_N_ELEMENTS (actions),
				      window);

	gtk_ui_manager_insert_action_group (priv->manager,
					    priv->action_group,
					    0);

	action = gtk_action_group_get_action (priv->action_group,
					      "Back");
	g_object_set (action, "sensitive", FALSE, NULL);

	action = gtk_action_group_get_action (priv->action_group,
					      "Forward");
	g_object_set (action, "sensitive", FALSE, NULL);

	accel_group = gtk_accel_group_new ();
	gtk_window_add_accel_group (GTK_WINDOW (window), accel_group);

	for (i = 0; i < G_N_ELEMENTS (tab_accel_keys); i++) {
		closure =  g_cclosure_new (G_CALLBACK (window_html_tab_accel_cb),
					   window,
					   NULL);
		gtk_accel_group_connect (accel_group,
					 tab_accel_keys[i],
					 GDK_MOD1_MASK,
					 0,
					 closure);
	}
}

static void
window_finalize (GObject *object)
{
	DhWindow *window = DH_WINDOW (object);
	DhBase *base = window->priv->base;

	G_OBJECT_CLASS (parent_class)->finalize (object);

	g_object_unref (base);
}

/* The ugliest hack. When switching tabs, the selection and cursor is changed
 * for the tree view so the html content is changed. Block the signal during
 * switch.
 */
static void
window_control_switch_page_cb (GtkWidget       *notebook,
			       GtkNotebookPage *page,
			       guint            page_num,
			       DhWindow        *window)
{
	DhWindowPriv *priv;

	priv = window->priv;

	g_signal_handlers_block_by_func (priv->book_tree,
					 window_tree_link_selected_cb, window);
}

static void
window_control_after_switch_page_cb (GtkWidget       *notebook,
				     GtkNotebookPage *page,
				     guint            page_num,
				     DhWindow        *window)
{
	DhWindowPriv *priv;

	priv = window->priv;

	g_signal_handlers_unblock_by_func (priv->book_tree,
					   window_tree_link_selected_cb, window);
}

static void
window_html_switch_page_cb (GtkNotebook     *notebook,
			    GtkNotebookPage *page,
			    guint            new_page_num,
			    DhWindow        *window)
{
	DhWindowPriv *priv;
	GtkWidget    *new_page;

	priv = window->priv;

	new_page = gtk_notebook_get_nth_page (notebook, new_page_num);
	if (new_page) {
		DhHtml *new_html;
		gchar  *title, *location;

		new_html = g_object_get_data (G_OBJECT (new_page), "html");

		window_update_title (window, new_html);

		return;



		title = dh_html_get_title (new_html);
		gtk_window_set_title (GTK_WINDOW (window), title);
		g_free (title);

		/* Sync the book tree. */
		location = dh_html_get_location (new_html);
		if (location) {
			dh_book_tree_select_uri (DH_BOOK_TREE (priv->book_tree),
						 location);
			g_free (location);
		}

		window_check_history (window, new_html);

	} else {
		gtk_window_set_title (GTK_WINDOW (window), "Devhelp");
		window_check_history (window, NULL);
	}
}

static void
window_populate (DhWindow *window)
{
        DhWindowPriv *priv;
	GtkWidget    *book_tree_sw;
	GNode        *contents_tree;
	GList        *keywords;
	gint          hpaned_position;

        priv = window->priv;

	gtk_ui_manager_add_ui_from_file (priv->manager,
					 DATADIR "/devhelp/ui/window.ui",
					 NULL);
	gtk_ui_manager_ensure_update (priv->manager);

        priv->hpaned   = gtk_hpaned_new ();

	gtk_box_pack_start (GTK_BOX (priv->main_box), priv->hpaned, TRUE, TRUE, 0);

	hpaned_position = gconf_client_get_int (priv->gconf_client,
						GCONF_PANED_LOCATION,
						NULL);

	/* This workaround for broken schema installs is not really working that
	 * well, since it makes having a 0 location not possible.
	 */
	if (hpaned_position <= 0) {
		hpaned_position = DEFAULT_PANED_LOC;
	}
 	gtk_paned_set_position (GTK_PANED (priv->hpaned), hpaned_position);

	/* Search and contents notebook. */
        priv->control_notebook = gtk_notebook_new ();

	gtk_paned_add1 (GTK_PANED (priv->hpaned), priv->control_notebook);

	g_signal_connect (priv->control_notebook,
			  "switch-page",
			  G_CALLBACK (window_control_switch_page_cb),
			  window);

	g_signal_connect_after (priv->control_notebook,
				"switch-page",
				G_CALLBACK (window_control_after_switch_page_cb),
				window);

	book_tree_sw = gtk_scrolled_window_new (NULL, NULL);

	gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (book_tree_sw),
					GTK_POLICY_NEVER,
					GTK_POLICY_AUTOMATIC);
	gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (book_tree_sw),
					     GTK_SHADOW_IN);
	gtk_container_set_border_width (GTK_CONTAINER (book_tree_sw), 2);

	contents_tree = dh_base_get_book_tree (priv->base);
	keywords = dh_base_get_keywords (priv->base);

	priv->book_tree = dh_book_tree_new (contents_tree);

	gtk_container_add (GTK_CONTAINER (book_tree_sw),
			   priv->book_tree);

	gtk_notebook_append_page (GTK_NOTEBOOK (priv->control_notebook),
				  book_tree_sw,
				  gtk_label_new (_("Contents")));
	g_signal_connect (priv->book_tree,
			  "link-selected",
			  G_CALLBACK (window_tree_link_selected_cb),
			  window);

	priv->search = dh_search_new (keywords);

	gtk_notebook_append_page (GTK_NOTEBOOK (priv->control_notebook),
				  priv->search,
				  gtk_label_new (_("Search")));

	g_signal_connect (priv->search,
			  "link-selected",
			  G_CALLBACK (window_search_link_selected_cb),
			  window);

	priv->vbox = gtk_vbox_new (FALSE, 0);
	gtk_paned_add2 (GTK_PANED (priv->hpaned), priv->vbox);

	/* HTML tabs notebook. */
 	priv->html_notebook = gtk_notebook_new ();
	gtk_box_pack_start (GTK_BOX (priv->vbox), priv->html_notebook, TRUE, TRUE, 0);

 	g_signal_connect (priv->html_notebook,
			  "switch-page",
			  G_CALLBACK (window_html_switch_page_cb),
			  window);

	/* Create findbar. */
	priv->findbar = egg_find_bar_new ();
	gtk_widget_set_no_show_all (priv->findbar, TRUE);
	gtk_box_pack_start (GTK_BOX (priv->vbox), priv->findbar, FALSE, FALSE, 0);

	g_signal_connect (priv->findbar,
			  "notify::search-string",
			  G_CALLBACK(window_findbar_search_changed_cb),
			  window);
	g_signal_connect (priv->findbar,
			  "notify::case-sensitive",
			  G_CALLBACK (window_findbar_case_sensitive_changed_cb),
			  window);
	g_signal_connect (priv->findbar,
			  "previous",
			  G_CALLBACK (window_find_previous_cb),
			  window);
	g_signal_connect (priv->findbar,
			  "next",
			  G_CALLBACK (window_find_next_cb),
			  window);
	g_signal_connect (priv->findbar,
			  "close",
			  G_CALLBACK (window_findbar_close_cb),
			  window);

	dh_preferences_setup_fonts ();

	gtk_widget_show_all (priv->hpaned);

	window_open_new_tab (window, NULL);
}

static void
window_activate_new_window (GtkAction *action, DhWindow *window)
{
	DhWindowPriv *priv;
	GtkWidget    *new_window;

	priv = window->priv;

	new_window = dh_base_new_window (priv->base);
	gtk_widget_show (new_window);
}

static void
window_activate_new_tab (GtkAction *action, DhWindow *window)
{
	DhWindowPriv *priv;

	priv = window->priv;

	window_open_new_tab (window, NULL);
}

static void
window_activate_close (GtkAction *action, DhWindow *window)
{
	DhWindowPriv *priv;
	gint          page_num;

	priv = window->priv;

	page_num = gtk_notebook_get_current_page (GTK_NOTEBOOK (priv->html_notebook));
        gtk_notebook_remove_page (GTK_NOTEBOOK (priv->html_notebook), page_num);

	if (gtk_notebook_get_n_pages (GTK_NOTEBOOK (priv->html_notebook)) == 0) {
		window_save_state (window);
		gtk_widget_destroy (GTK_WIDGET (window));
	}
}

static void
window_activate_quit (GtkAction *action, DhWindow *window)
{
	gtk_main_quit ();
}

static void
window_activate_copy (GtkAction *action, DhWindow *window)
{
	GtkWidget *widget;

	widget = gtk_window_get_focus (GTK_WINDOW (window));

	if (GTK_IS_EDITABLE (widget)) {
		gtk_editable_copy_clipboard (GTK_EDITABLE (widget));
	} else {
		DhHtml *html;

		html = window_get_active_html (window);
		dh_html_copy_selection (html);
	}
}

static void
window_activate_find (GtkAction *action,
		      DhWindow  *window)
{
	DhWindowPriv *priv;
	DhHtml       *html;

	priv = window->priv;

	html = window_get_active_html (window);

	dh_html_search_find (
		html, egg_find_bar_get_search_string (EGG_FIND_BAR (priv->findbar)));
	dh_html_search_set_case_sensitive (
		html,  egg_find_bar_get_case_sensitive (EGG_FIND_BAR (priv->findbar)));

	gtk_widget_show (priv->findbar);
	gtk_widget_grab_focus (priv->findbar);
}

static void
window_activate_preferences (GtkAction *action,
			     DhWindow  *window)
{
	dh_preferences_show_dialog (GTK_WINDOW (window));
}

static void
window_activate_back (GtkAction *action,
		      DhWindow  *window)
{
	DhWindowPriv *priv;
	DhHtml       *html;
	GtkWidget    *frame;

	priv = window->priv;

	frame = gtk_notebook_get_nth_page (
		GTK_NOTEBOOK (priv->html_notebook),
		gtk_notebook_get_current_page (GTK_NOTEBOOK (priv->html_notebook)));
	html = g_object_get_data (G_OBJECT (frame), "html");

	dh_html_go_back (html);
}

static void
window_activate_forward (GtkAction *action,
			 DhWindow  *window)
{
	DhWindowPriv *priv;
	DhHtml       *html;
	GtkWidget    *frame;

	priv = window->priv;

	frame = gtk_notebook_get_nth_page (GTK_NOTEBOOK (priv->html_notebook),
	                                   gtk_notebook_get_current_page (GTK_NOTEBOOK (priv->html_notebook))
	                                  );
	html = g_object_get_data (G_OBJECT (frame), "html");

	dh_html_go_forward (html);
}

static void
window_activate_show_contents (GtkAction *action,
			       DhWindow  *window)
{
	DhWindowPriv *priv;

	priv = window->priv;

	gtk_notebook_set_current_page (GTK_NOTEBOOK (priv->control_notebook), 0);
	gtk_widget_grab_focus (priv->book_tree);
}

static void
window_activate_show_search (GtkAction *action,
			     DhWindow  *window)
{
	DhWindowPriv *priv;

	priv = window->priv;

	gtk_notebook_set_current_page (GTK_NOTEBOOK (priv->control_notebook), 1);
	dh_search_grab_focus (DH_SEARCH (priv->search));
}

static void
window_activate_about (GtkAction *action,
		       DhWindow  *window)
{
	const gchar  *authors[] = {
		"Mikael Hallendal <micke@imendio.com>",
		"Richard Hult <richard@imendio.com>",
		"Johan Dahlin <johan@gnome.org>",
		"Ross Burton <ross@burtonini.com>",
		NULL
	};
	const gchar **documenters = NULL;
	const gchar  *translator_credits = _("translator_credits");

	gtk_show_about_dialog (GTK_WINDOW (window),
			       "name",_("Devhelp"),
			       "version", VERSION,
			       "comments", _("A developer's help browser for GNOME 2"),
			       "authors", authors,
			       "documenters", documenters,
			       "translator-credits",
			       strcmp (translator_credits, "translator_credits") != 0 ?
			       translator_credits : NULL,
			       "website", "http://developer.imendio.com/wiki/Devhelp",
			       "logo-icon-name", "devhelp",
			       NULL);
}

static void
window_save_state (DhWindow *window)
{
	DhWindowPriv   *priv;
	GdkWindowState  state;
	gboolean        maximized;

	priv = window->priv;

	state = gdk_window_get_state (GTK_WIDGET (window)->window);
	if (state & GDK_WINDOW_STATE_MAXIMIZED) {
		maximized = TRUE;
	} else {
		maximized = FALSE;
	}

	gconf_client_set_bool (priv->gconf_client,
			       GCONF_MAIN_WINDOW_MAXIMIZED, maximized,
			       NULL);

	/* If maximized don't save the size and position */
	if (!maximized) {
		gint width, height;
		gint x, y;

		gtk_window_get_size (GTK_WINDOW (window), &width, &height);
		gconf_client_set_int (priv->gconf_client,
				      GCONF_MAIN_WINDOW_WIDTH, width,
				      NULL);
		gconf_client_set_int (priv->gconf_client,
				      GCONF_MAIN_WINDOW_HEIGHT, height,
				      NULL);

		gtk_window_get_position (GTK_WINDOW (window), &x, &y);
		gconf_client_set_int (priv->gconf_client,
				      GCONF_MAIN_WINDOW_POS_X, x,
				      NULL);
		gconf_client_set_int (priv->gconf_client,
				      GCONF_MAIN_WINDOW_POS_Y, y,
				      NULL);
	}

	gconf_client_set_int (priv->gconf_client,
			      GCONF_PANED_LOCATION,
			      gtk_paned_get_position (GTK_PANED (priv->hpaned)),
			      NULL);

	if (gtk_notebook_get_current_page (GTK_NOTEBOOK (priv->control_notebook)) == 0) {
		gconf_client_set_string (priv->gconf_client,
					 GCONF_SELECTED_TAB, "content",
					 NULL);
	} else {
		gconf_client_set_string (priv->gconf_client,
					 GCONF_SELECTED_TAB, "search",
					 NULL);
	}
}

static void
window_restore_state (DhWindow *window)
{
	DhWindowPriv *priv;
	gboolean      maximized;
	int           width, height;
	int           x, y;
	gchar        *tab;

	priv = window->priv;

	width = gconf_client_get_int (priv->gconf_client,
				      GCONF_MAIN_WINDOW_WIDTH,
				      NULL);

	if (width <= 0) {
		width = DEFAULT_WIDTH;
	}

	height = gconf_client_get_int (priv->gconf_client,
				       GCONF_MAIN_WINDOW_HEIGHT,
				       NULL);

	if (height <= 0) {
		height = DEFAULT_HEIGHT;
	}

	gtk_window_set_default_size (GTK_WINDOW (window),
				     width, height);

	x = gconf_client_get_int (priv->gconf_client,
				  GCONF_MAIN_WINDOW_POS_X,
				  NULL);
	y = gconf_client_get_int (priv->gconf_client,
				  GCONF_MAIN_WINDOW_POS_Y,
				  NULL);

	gtk_window_move (GTK_WINDOW (window), x, y);

	maximized = gconf_client_get_bool (priv->gconf_client,
					   GCONF_MAIN_WINDOW_MAXIMIZED,
					   NULL);
	if (maximized) {
		gtk_window_maximize (GTK_WINDOW (window));
	}

	tab = gconf_client_get_string (priv->gconf_client,
				       GCONF_SELECTED_TAB,
				       NULL);
	if (!tab || strcmp (tab, "") == 0 || strcmp (tab, "content") == 0) {
		gtk_notebook_set_current_page (GTK_NOTEBOOK (priv->control_notebook), 0);
		gtk_widget_grab_focus (priv->book_tree);
	} else {
		gtk_notebook_set_current_page (GTK_NOTEBOOK (priv->control_notebook), 1);
		dh_search_grab_focus (DH_SEARCH (priv->search));
	}
	g_free (tab);
}

static gboolean
window_delete_cb (GtkWidget   *widget,
		  GdkEventAny *event,
		  gpointer     user_data)
{
	window_save_state (DH_WINDOW (widget));

	return FALSE;
}

static void
window_tree_link_selected_cb (GObject  *ignored,
			      DhLink   *link,
			      DhWindow *window)
{
	DhWindowPriv *priv;
	DhHtml       *html;

	priv = window->priv;

	html = window_get_active_html (window);

	/* Block so we don't try to sync the tree when we have already clicked
	 * in it.
	 */
	g_signal_handlers_block_by_func (html,
					 window_html_open_uri_cb,
					 window);

	dh_html_open_uri (html, link->uri);

	g_signal_handlers_unblock_by_func (html,
					   window_html_open_uri_cb,
					   window);

	window_check_history (window, html);
}

static void
window_search_link_selected_cb (GObject  *ignored,
				DhLink   *link,
				DhWindow *window)
{
	DhWindowPriv *priv;
	DhHtml       *html;

	priv = window->priv;

	html = window_get_active_html (window);

	dh_html_open_uri (html, link->uri);

	window_check_history (window, html);
}

static void
window_manager_add_widget (GtkUIManager *manager,
			   GtkWidget    *widget,
			   DhWindow     *window)
{
	DhWindowPriv *priv;

	priv = window->priv;

	gtk_box_pack_start (GTK_BOX (priv->menu_box), widget,
			    FALSE, FALSE, 0);

	gtk_widget_show (widget);
}

static void
window_check_history (DhWindow *window, DhHtml *html)
{
	DhWindowPriv *priv;
	GtkAction    *action;

	priv = window->priv;

	action = gtk_action_group_get_action (priv->action_group, "Forward");
	g_object_set (action,
		      "sensitive", html ? dh_html_can_go_forward (html) : FALSE,
		      NULL);

	action = gtk_action_group_get_action (priv->action_group, "Back");
	g_object_set (action,
		      "sensitive", html ? dh_html_can_go_back (html) : FALSE,
		      NULL);
}

static void
window_html_location_changed_cb (DhHtml      *html,
				 const gchar *location,
				 DhWindow    *window)
{
	DhWindowPriv *priv;

	priv = window->priv;

	if (html == window_get_active_html (window)) {
		window_check_history (window, html);
	}
}

static gboolean
window_html_open_uri_cb (DhHtml      *html,
			 const gchar *uri,
			 DhWindow    *window)
{
	DhWindowPriv *priv;

	priv = window->priv;

	if (html == window_get_active_html (window)) {
		dh_book_tree_select_uri (DH_BOOK_TREE (priv->book_tree), uri);
	}

	return FALSE;
}

static void
window_html_title_changed_cb (DhHtml      *html,
			      const gchar *title,
			      DhWindow    *window)
{
	window_update_title (window, window_get_active_html (window));
}

static void
window_findbar_search_changed_cb (GObject    *object,
				  GParamSpec *pspec,
				  DhWindow   *window)
{
	DhWindowPriv *priv;
	DhHtml       *html;

	priv = window->priv;

	html = window_get_active_html (window);

	dh_html_search_find (html,
		egg_find_bar_get_search_string (EGG_FIND_BAR (priv->findbar)));
}

static void
window_findbar_case_sensitive_changed_cb (GObject    *object,
					  GParamSpec *pspec,
					  DhWindow   *window)
{
	DhWindowPriv *priv;
	DhHtml       *html;

	priv = window->priv;

	html = window_get_active_html (window);

	dh_html_search_set_case_sensitive (
		html,
		egg_find_bar_get_case_sensitive (EGG_FIND_BAR (priv->findbar)));
}

static void
window_find_next_cb (GtkEntry *entry,
		     DhWindow *window)
{
	DhHtml       *html;
	DhWindowPriv *priv = window->priv;

	priv = window->priv;

	gtk_widget_show (priv->findbar);

	html = window_get_active_html (window);

	dh_html_search_find_again (html, FALSE);
}

static void
window_find_previous_cb (GtkEntry *entry,
			 DhWindow *window)
{
	DhHtml       *html;
	DhWindowPriv *priv = window->priv;

	priv = window->priv;

	gtk_widget_show (priv->findbar);

	html = window_get_active_html (window);

	dh_html_search_find_again (html, TRUE);
}

static void
window_findbar_close_cb (GtkWidget *widget,
			 DhWindow  *window)
{
	DhWindowPriv *priv;

	priv = window->priv;

	gtk_widget_hide (priv->findbar);
}

static void
window_html_open_new_tab_cb (DhHtml      *html,
			     const gchar *location,
			     DhWindow    *window)
{
	window_open_new_tab (window, location);
}

static void
window_html_tab_accel_cb (GtkAccelGroup    *accel_group,
			  GObject          *object,
			  guint             key,
			  GdkModifierType   mod,
			  DhWindow         *window)
{
	DhWindowPriv *priv;
	gint          i, num;

	priv = window->priv;

	num = -1;
	for (i = 0; i < G_N_ELEMENTS (tab_accel_keys); i++) {
		if (tab_accel_keys[i] == key) {
			num = i;
			break;
		}
	}

	if (num != -1) {
		gtk_notebook_set_current_page (
			GTK_NOTEBOOK (priv->html_notebook), num);
	}
}

static void
window_open_new_tab (DhWindow    *window,
		     const gchar *location)
{
	DhWindowPriv *priv;
	DhHtml       *html;
	GtkWidget    *frame;
	GtkWidget    *view;
	GtkWidget    *label;
	gint          num;

	priv = window->priv;

	html = dh_html_new ();

	view = dh_html_get_widget (html);
	gtk_widget_show (view);

	frame = gtk_frame_new (NULL);
	gtk_widget_show (frame);


	gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
	gtk_container_set_border_width (GTK_CONTAINER (frame), 2);

	gtk_container_add (GTK_CONTAINER (frame), view);

	g_object_set_data (G_OBJECT (frame), "html", html);

	label = window_new_tab_label (window, _("Empty Page"));
	gtk_widget_show (label);

	g_signal_connect (html, "title-changed",
			  G_CALLBACK (window_html_title_changed_cb),
			  window);
	g_signal_connect (html, "open-uri",
			  G_CALLBACK (window_html_open_uri_cb),
			  window);
	g_signal_connect (html, "location-changed",
			  G_CALLBACK (window_html_location_changed_cb),
			  window);
	g_signal_connect (html, "open-new-tab",
			  G_CALLBACK (window_html_open_new_tab_cb),
			  window);

	num = gtk_notebook_append_page (GTK_NOTEBOOK (priv->html_notebook),
				  frame, NULL);

	gtk_notebook_set_tab_label (GTK_NOTEBOOK (priv->html_notebook),
				    frame, label);

	gtk_notebook_set_tab_label_packing (GTK_NOTEBOOK (priv->html_notebook),
					    frame,
					    TRUE, TRUE,
					    GTK_PACK_START);

	/* Hack to get GtkMozEmbed to work properly. */
	gtk_widget_realize (view);

	if (location) {
		dh_html_open_uri (html, location);
	} else {
		dh_html_clear (html);
	}
	gtk_notebook_set_current_page (GTK_NOTEBOOK (priv->html_notebook), num);

}

static GtkWidget*
window_new_tab_label (DhWindow *window, const gchar *str)
{
	GtkWidget *label;

	label = gtk_label_new (str);
	gtk_label_set_ellipsize (GTK_LABEL (label), PANGO_ELLIPSIZE_END);
	gtk_label_set_single_line_mode (GTK_LABEL (label), TRUE);
	gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
	gtk_misc_set_padding (GTK_MISC (label), 0, 0);

	return label;
}

static DhHtml *
window_get_active_html (DhWindow *window)
{
	DhWindowPriv *priv;
	gint          page_num;
	GtkWidget    *page;

	priv = window->priv;

	page_num = gtk_notebook_get_current_page (GTK_NOTEBOOK (priv->html_notebook));
	if (page_num == -1) {
		return NULL;
	}

	page = gtk_notebook_get_nth_page (GTK_NOTEBOOK (priv->html_notebook), page_num);

	return g_object_get_data (G_OBJECT (page), "html");
}

static void
window_update_title (DhWindow *window,
		     DhHtml   *html)
{
	DhWindowPriv *priv;
	gchar        *html_title;
	const gchar  *book_title;
	gchar        *full_title;

	priv = window->priv;

	html_title = dh_html_get_title (html);

	window_tab_set_title (window, html, html_title);

	if (html_title && *html_title == '\0') {
		g_free (html_title);
		html_title = NULL;
	}

	book_title = dh_book_tree_get_selected_book_title (DH_BOOK_TREE (priv->book_tree));

	/* Don't use both titles if they are the same. */
	if (book_title && html_title && strcmp (book_title, html_title) == 0) {
		g_free (html_title);
		html_title = NULL;
	}

	if (!book_title) {
		book_title = "Devhelp";
	}

	if (html_title) {
		full_title = g_strdup_printf ("%s : %s", book_title, html_title);
		g_free (html_title);
	} else {
		full_title = g_strdup (book_title);
	}

	gtk_window_set_title (GTK_WINDOW (window), full_title);
	g_free (full_title);
}

static void
window_tab_set_title (DhWindow *window, DhHtml *html, const gchar *title)
{
	DhWindowPriv *priv;
	gint          num_pages, i;
	GtkWidget    *view;
	GtkWidget    *page;
	GtkWidget    *label;

	priv = window->priv;

	view = dh_html_get_widget (html);

	if (!title || title[0] == '\0') {
		title = _("Empty Page");
	}

	num_pages = gtk_notebook_get_n_pages (GTK_NOTEBOOK (priv->html_notebook));
	for (i = 0; i < num_pages; i++) {
		page = gtk_notebook_get_nth_page (
			GTK_NOTEBOOK (priv->html_notebook), i);

		/* The html widget is inside a frame. */
		if (gtk_bin_get_child (GTK_BIN (page)) == view) {
			label = gtk_notebook_get_tab_label (
				GTK_NOTEBOOK (priv->html_notebook), page);

			if (label) {
				gtk_label_set_text (GTK_LABEL (label), title);
			}
			break;
		}
	}
}

GtkWidget *
dh_window_new (DhBase *base)
{
        DhWindow     *window;
        DhWindowPriv *priv;

        window = g_object_new (DH_TYPE_WINDOW, NULL);
        priv = window->priv;

	priv->base = g_object_ref (base);

	g_signal_connect (window,
			  "delete-event",
			  G_CALLBACK (window_delete_cb),
			  NULL);

	window_populate (window);
	window_restore_state (window);

	gtk_window_set_icon_name (GTK_WINDOW (window), "devhelp");

	return GTK_WIDGET (window);
}

void
dh_window_search (DhWindow *window, const gchar *str)
{
	DhWindowPriv *priv;

	g_return_if_fail (DH_IS_WINDOW (window));

	priv = window->priv;

	gtk_notebook_set_current_page (GTK_NOTEBOOK (priv->control_notebook), 1);

	dh_search_set_search_string (DH_SEARCH (priv->search), str);
}

void
dh_window_focus_search (DhWindow *window)
{
	DhWindowPriv *priv;

	g_return_if_fail (DH_IS_WINDOW (window));

	priv = window->priv;

	gtk_notebook_set_current_page (GTK_NOTEBOOK (priv->control_notebook), 1);

	dh_search_grab_focus (DH_SEARCH (priv->search));
}


syntax highlighted by Code2HTML, v. 0.9.1