/* eLectrix - a pdf viewer
 * Copyright (C) 2010, 2011 Martin Linder <mali2297@users.sf.net>
 * 
 * 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, see <http://www.gnu.org/licenses/gpl-2.0.html>.
 */
#include <gdk/gdkkeysyms.h>
#include "e6x-common.h"
#include "e6x-page-selector.h"
#include "e6x-pref.h"
#include "e6x-scale-selector.h"
#include "e6x-util.h"
#include "e6x-dialogs.h"
#include "e6x-view.h"
#include "e6x-att-view.h"
#include "e6x-toc-view.h"
#include "e6x-enc-document.h"
#include "e6x-window.h"

#define E6X_WINDOW_GET_PRIVATE(o) \
  (G_TYPE_INSTANCE_GET_PRIVATE ((o), E6X_TYPE_WINDOW, E6xWindowPrivate))

struct _E6xWindowPrivate
{
  E6xDocument *doc;
  GtkWidget *notebook;
  GtkWidget *paned;
  GtkWidget *pane1;
  GtkWidget *pane2;
  GtkWidget *tocview;
  GtkWidget *view;
  GtkWidget *menubar;
  GtkWidget *right_click_menu;
  GtkWidget *toolbar;
  GtkWidget *findbar;
  GtkWidget *find_entry;
  GtkWidget *find_status_label;
  GtkWidget *page_selector;
  GtkWidget *scale_selector;
  GtkUIManager *ui_manager;
  gboolean fullscreen;
};

static struct
{
  gdouble zoom_min;
  gdouble zoom_max;
  gdouble zoom_step;
  gdouble zoom_default;
  gboolean show_toolbar;
} e6x_window_settings = 
{
  0.25, 
  4.0, 
  0.25, 
  1.0, 
  TRUE
};

/* Standard GObject methods */
G_DEFINE_TYPE (E6xWindow, e6x_window, GTK_TYPE_WINDOW)
static void e6x_window_class_init (E6xWindowClass *klass);
static void e6x_window_init (E6xWindow *win);
static void e6x_window_finalize (GObject *object);
static void e6x_window_dispose (GObject *object);

/* Callbacks to menu or accelerator actions */
static void e6x_window_open (GtkAction *action, 
                             E6xWindow *win);
static void e6x_window_open_recent (GtkRecentChooser *chooser,
                                    E6xWindow *win);
static void e6x_window_open_in_new_window (GtkAction *action, 
                                           E6xWindow *win);
static void e6x_window_decrypt (GtkAction *action, 
                                E6xWindow *win);
static void e6x_window_save_copy (GtkAction *action, 
                                  E6xWindow *win);
static void e6x_window_print (GtkAction *action, 
                              E6xWindow *win);
static void e6x_window_show_properties (GtkAction *action, 
                                        E6xWindow *win);
static void e6x_window_show_attachments (GtkAction *action, 
                                         E6xWindow *win);
static void e6x_window_close_tab (GtkAction *action, 
                                  E6xWindow *win);
static void e6x_window_close_window (GtkAction *action, 
                                     E6xWindow *win);
static void e6x_window_quit (GtkAction *action, 
                             E6xWindow *win);
static void e6x_window_select_text (GtkAction *action,
                                    E6xWindow *win);
static void e6x_window_find (GtkAction *action,
                             E6xWindow *win);
static void e6x_window_reload (GtkAction *action, 
                               E6xWindow *win);
static void e6x_window_zoom (GtkAction *action, 
                             E6xWindow *win);
static void e6x_window_rotate (GtkAction *action,
                               E6xWindow *win);
static void e6x_window_toggle_color (GtkAction *action,
                                     E6xWindow *win);
static void e6x_window_toggle_fit (GtkAction *action, 
                                   E6xWindow *win);
static void e6x_window_toggle_fullscreen (GtkAction *action, 
                                          E6xWindow *win);
static void e6x_window_toggle_toolbar (GtkAction *action, 
                                       E6xWindow *win);
static void e6x_window_toggle_toc (GtkAction *action, 
                                   E6xWindow *win);
static void e6x_window_switch_page (GtkAction *action, 
                                    E6xWindow *win);
static void e6x_window_about (GtkAction *action, 
                              E6xWindow *win);
static void e6x_window_switch_tab (GtkAction *action, 
                                   E6xWindow *win);
static void e6x_window_walk_history (GtkAction *action, 
                                     E6xWindow *win);

/* Other callbacks */
static void e6x_window_uri_dropped (GtkWidget *widget,
                                    GdkDragContext *drag_context,
                                    gint x,
                                    gint y,
                                    GtkSelectionData *data,
                                    guint info,
                                    guint time);
static void  e6x_window_close_button_clicked (GtkButton *button, 
                                              GtkWidget *child);
static void e6x_window_find_activate (GtkEntry *entry, 
                                      E6xWindow *win);
static gboolean e6x_window_find_key_press_event (GtkWidget *bar,
                                                 GdkEventKey *event);
static void  e6x_window_hide_findbar (GtkToolButton *button, 
                                      GtkWidget *bar);
static void e6x_window_tab_switched (GtkNotebook *notebook,
                                     gpointer *tab,
                                     guint tab_num,
                                     E6xWindow *win);
static void e6x_window_n_tabs_changed (GtkNotebook *notebook,
                                       gpointer *tab,
                                       guint tab_num,
                                       E6xWindow *win);
static void e6x_window_pane_size_changed (GtkWidget *pane,
                                          GdkRectangle *allocation,
                                          E6xWindow *win);
static void e6x_window_update_ui (E6xDocument *doc, 
                                  E6xWindow *win);
static gboolean e6x_window_button_press_event (GtkWidget *widget,
                                               GdkEventButton *event);


/* Custom helpers */
static GtkWidget *e6x_window_new_tab (E6xWindow *win);
static void e6x_window_add_recent (const gchar *filename);
static void e6x_window_find_iterate (E6xWindow *win,
                                     E6xDirection dir);
static GList *e6x_window_list ();
static void e6x_window_load_settings ();
static void e6x_window_set_title (E6xWindow *win,
                                  const gchar *text);
static GtkWidget *e6x_window_create_tab_label (const gchar *text,
                                               GtkWidget *child);
static void e6x_window_toggle_show_tabs (E6xWindow *win);
static void e6x_window_update_toc_view (E6xWindow *win);
static void e6x_window_update_fit (E6xWindow *win);

/* String arrays for user interface definition */
static const GtkActionEntry action_entries [] =
{
  { "file-menu", NULL, N_("_File") },
  { "open-file", GTK_STOCK_OPEN, NULL, NULL, NULL,
      G_CALLBACK (e6x_window_open) },
  { "new-window", NULL, N_("Open in _New Window"), NULL, NULL,
      G_CALLBACK (e6x_window_open_in_new_window) },
  { "decrypt", NULL, N_("_Decrypt"), NULL, NULL,
      G_CALLBACK (e6x_window_decrypt) },
  { "save-copy", GTK_STOCK_SAVE, N_("_Save Copy"), "<control>s", NULL,
      G_CALLBACK (e6x_window_save_copy) },
  { "print", GTK_STOCK_PRINT, NULL, "<control>p", NULL,
      G_CALLBACK (e6x_window_print) },
  { "show-properties", GTK_STOCK_PROPERTIES, NULL, NULL, NULL, 
      G_CALLBACK (e6x_window_show_properties) },
  { "show-attachments", NULL, N_("_Attachments"), NULL, NULL, 
      G_CALLBACK (e6x_window_show_attachments) },
  { "close-tab", GTK_STOCK_CLOSE, N_("_Close Tab"), NULL, NULL,
      G_CALLBACK (e6x_window_close_tab) },
  { "close-window", NULL, N_("Close Win_dow"), NULL, NULL,
      G_CALLBACK (e6x_window_close_window) },
  { "quit", GTK_STOCK_QUIT, NULL, NULL, NULL, 
      G_CALLBACK (e6x_window_quit) },
  { "edit-menu", NULL, N_("_Edit") },
  { "select-text", NULL, N_("_Select Text"), "<control>Insert", NULL, 
      G_CALLBACK (e6x_window_select_text) },
  { "find-text", GTK_STOCK_FIND, NULL, NULL, NULL, 
      G_CALLBACK (e6x_window_find) },
  { "find-prev", GTK_STOCK_MEDIA_PREVIOUS, N_("Find _Previous"), "<shift>F3", NULL, 
      G_CALLBACK (e6x_window_find) },
  { "find-next", GTK_STOCK_MEDIA_NEXT, N_("Find _Next"), "F3", NULL, 
      G_CALLBACK (e6x_window_find) },
  { "view-menu", NULL, N_("_View") },
  { "reload", GTK_STOCK_REVERT_TO_SAVED, N_("_Reload"), "<control>r", NULL, 
      G_CALLBACK (e6x_window_reload) },
  { "zoom-in", GTK_STOCK_ZOOM_IN, NULL, "<control>plus", NULL, 
      G_CALLBACK (e6x_window_zoom) },
  { "zoom-out", GTK_STOCK_ZOOM_OUT, NULL, "<control>minus", NULL, 
      G_CALLBACK (e6x_window_zoom) },
  { "zoom-reset", GTK_STOCK_ZOOM_100, NULL, "<control>0", NULL,
      G_CALLBACK (e6x_window_zoom) },
  { "rotate-cw", NULL, N_("Rotate _Clockwise"), "<alt>plus", NULL,
      G_CALLBACK (e6x_window_rotate) },
  { "rotate-ccw", NULL, N_("Rotate Co_unterclockwise"), "<alt>minus", NULL,
      G_CALLBACK (e6x_window_rotate) },
  { "go-menu", NULL, N_("_Go") },
  { "prev", GTK_STOCK_GO_UP, N_("_Previous Page"), "<control>Prior", NULL, 
      G_CALLBACK (e6x_window_switch_page) },
  { "next", GTK_STOCK_GO_DOWN, N_("_Next Page"), "<control>Next", NULL, 
      G_CALLBACK (e6x_window_switch_page) },
  { "first", GTK_STOCK_GOTO_TOP, N_("_First Page"), "<control>Home", NULL, 
      G_CALLBACK (e6x_window_switch_page) },
  { "last", GTK_STOCK_GOTO_BOTTOM, N_("_Last Page"), "<control>End", NULL, 
      G_CALLBACK (e6x_window_switch_page) },
  { "history-back", GTK_STOCK_GO_BACK, N_("Go _Back"), "<alt>Left", NULL, 
      G_CALLBACK (e6x_window_walk_history) },
  { "history-forward", GTK_STOCK_GO_FORWARD, N_("Go _Forward"), "<alt>Right", NULL, 
      G_CALLBACK (e6x_window_walk_history) },
  { "help-menu", NULL, N_("_Help") },
  { "about", GTK_STOCK_ABOUT, NULL, NULL, NULL,
      G_CALLBACK (e6x_window_about) },
  { "tab-prev", NULL, NULL, "<shift><control>Prior", NULL, 
      G_CALLBACK (e6x_window_switch_tab) },
  { "tab-next", NULL, NULL, "<shift><control>Next", NULL, 
      G_CALLBACK (e6x_window_switch_tab) }
};

static const GtkToggleActionEntry toggle_action_entries [] =
{
  { "fit-page", GTK_STOCK_ZOOM_FIT, NULL, NULL, NULL, 
      G_CALLBACK (e6x_window_toggle_fit), FALSE },
  { "fit-width", NULL, N_("Fit _Width"), NULL, NULL, 
      G_CALLBACK (e6x_window_toggle_fit), FALSE },
  { "color-inversion", NULL, N_("_Invert Color"), NULL, NULL, 
      G_CALLBACK (e6x_window_toggle_color), FALSE },
  { "fullscreen", GTK_STOCK_FULLSCREEN, NULL, "F11", NULL,
      G_CALLBACK (e6x_window_toggle_fullscreen), FALSE },
  { "show-toolbar", NULL, N_("_Toolbar"), NULL, NULL,
      G_CALLBACK (e6x_window_toggle_toolbar), TRUE },
  { "show-toc", GTK_STOCK_INDEX, NULL, "<control>b", NULL,
      G_CALLBACK (e6x_window_toggle_toc), FALSE }
};

static const gchar* ui_markup = 
  "<ui>"
  "<menubar name='main-menu'>"
  "<menu action='file-menu'>"
  "<menuitem action='open-file'/>"
  "<menuitem action='recent-files'/>"
  "<menuitem action='new-window'/>"
  "<menuitem action='decrypt'/>"
  "<menuitem action='save-copy'/>"
  "<separator/>"
  "<menuitem action='print'/>"
  "<separator/>"
  "<menuitem action='show-properties'/>"
  "<menuitem action='show-attachments'/>"
  "<separator/>"
  "<menuitem action='close-tab'/>"
  "<menuitem action='close-window'/>"
  "<menuitem action='quit'/>"
  "</menu>"
  "<menu action='edit-menu'>"
  "<menuitem action='select-text'/>"
  "<separator/>"
  "<menuitem action='find-text'/>"
  "<menuitem action='find-prev'/>"
  "<menuitem action='find-next'/>"
  "</menu>"
  "<menu action='view-menu'>"
  "<menuitem action='reload'/>"
  "<separator/>"
  "<menuitem action='fit-page'/>"
  "<menuitem action='fit-width'/>"
  "<menuitem action='zoom-in'/>"
  "<menuitem action='zoom-out'/>"
  "<menuitem action='zoom-reset'/>"
  "<separator/>"
  "<menuitem action='rotate-cw'/>"
  "<menuitem action='rotate-ccw'/>"
  "<separator/>"
  "<menuitem action='color-inversion'/>"
  "<separator/>"
  "<menuitem action='show-toolbar'/>"
  "<menuitem action='show-toc'/>"
  "<menuitem action='fullscreen'/>"
  "</menu>"
  "<menu action='go-menu'>"
  "<menuitem action='first'/>"
  "<menuitem action='prev'/>"
  "<menuitem action='next'/>"
  "<menuitem action='last'/>"
  "<separator/>"
  "<menuitem action='history-back'/>"
  "<menuitem action='history-forward'/>"
  "</menu>"
  "<menu action='help-menu'>"
  "<menuitem action='about'/>"
  "</menu>"
  "</menubar>"

  "<popup name='right-click-menu'>"
  "<menuitem action='history-back'/>"
  "<menuitem action='history-forward'/>"
  "<separator/>"
  "<menuitem action='fit-page'/>"
  "<menuitem action='zoom-in'/>"
  "<menuitem action='zoom-out'/>"
  "<menuitem action='zoom-reset'/>"
  "<separator/>"
  "<menuitem action='rotate-cw'/>"
  "<separator/>"
  "<menuitem action='show-toc'/>"
  "<menuitem action='fullscreen'/>"
  "<separator/>"
  "<menuitem action='quit'/>"
  "</popup>"

  "<accelerator action='open-file'/>"
  "<accelerator action='reload'/>"
  "<accelerator action='decrypt'/>"
  "<accelerator action='save-copy'/>"
  "<accelerator action='print'/>"
  "<accelerator action='show-properties'/>"
  "<accelerator action='show-attachments'/>"
  "<accelerator action='quit'/>"
  "<accelerator action='select-text'/>"
  "<accelerator action='find-text'/>"
  "<accelerator action='find-next'/>"
  "<accelerator action='find-prev'/>"
  "<accelerator action='zoom-in'/>"
  "<accelerator action='zoom-out'/>"
  "<accelerator action='fit-page'/>"
  "<accelerator action='fit-width'/>"
  "<accelerator action='zoom-reset'/>"
  "<accelerator action='rotate-cw'/>"
  "<accelerator action='rotate-ccw'/>"
  "<accelerator action='color-inversion'/>"
  "<accelerator action='show-toolbar'/>"
  "<accelerator action='show-toc'/>"
  "<accelerator action='fullscreen'/>"
  "<accelerator action='prev'/>"
  "<accelerator action='next'/>"
  "<accelerator action='history-back'/>"
  "<accelerator action='history-forward'/>"
  "<accelerator action='first'/>"
  "<accelerator action='last'/>"
  "<accelerator action='about'/>"
  "<accelerator action='tab-prev'/>"
  "<accelerator action='tab-next'/>"
  "</ui>";


GtkWidget *
e6x_window_new ()
{
  return g_object_new (E6X_TYPE_WINDOW, "type", GTK_WINDOW_TOPLEVEL, NULL);
}


void 
e6x_window_add_document (E6xWindow *win, 
                         E6xDocument *doc)
{
  if (win->priv->doc != NULL)
    e6x_window_new_tab (win);
  e6x_window_set_document (win, doc);
}


void 
e6x_window_set_document (E6xWindow *win, 
                         E6xDocument *doc)
{
  E6xWindowPrivate *priv = win->priv;
  gchar *titletext = _("(Untitled)");
  
  if (priv->doc != NULL)
  {
    g_signal_handlers_disconnect_by_func (priv->doc,
                                          e6x_window_update_ui,
                                          win);
    g_object_unref (priv->doc);
  }

  priv->doc = doc;
  
  if (priv->doc != NULL)
  {
    g_object_ref (priv->doc);
    titletext = priv->doc->title;
    e6x_view_set_document (E6X_VIEW (priv->view), priv->doc);
    e6x_window_add_recent (priv->doc->filename);
    g_signal_connect (priv->doc, "changed",
                      G_CALLBACK (e6x_window_update_ui), win);
    g_signal_connect (priv->doc, "fit-toggled",
                      G_CALLBACK (e6x_window_update_ui), win);
  }
  
  e6x_window_set_title (win, titletext);
  if (priv->notebook != NULL && priv->paned != NULL)
  {
    GtkWidget *label = NULL;
    
    label = e6x_window_create_tab_label (titletext, priv->paned);
    gtk_notebook_set_tab_label (GTK_NOTEBOOK (priv->notebook), 
                                priv->paned, label);
    e6x_window_update_toc_view (win);
  }
  if (priv->page_selector != NULL)
  {
    e6x_page_selector_set_document (E6X_PAGE_SELECTOR (priv->page_selector), 
                                    priv->doc);
  }
  if (priv->scale_selector != NULL)
  {
    e6x_scale_selector_set_document (E6X_SCALE_SELECTOR (priv->scale_selector), 
                                     priv->doc); 
  }
  if (priv->ui_manager != NULL)
  {
    e6x_window_update_ui (priv->doc, win);
  }
}

static void
e6x_window_class_init (E6xWindowClass *klass)
{
  g_type_class_add_private (klass, sizeof (E6xWindowPrivate));

  GObjectClass *object_class = G_OBJECT_CLASS (klass);

  object_class->dispose = e6x_window_dispose;
  object_class->finalize = e6x_window_finalize;
}


static void
e6x_window_init (E6xWindow *win)
{
  E6xWindowPrivate *priv = E6X_WINDOW_GET_PRIVATE (win);
  GdkScreen *screen = NULL;
  GtkToolItem *toolitem = NULL;
  GtkAction *action = NULL;
  GtkRecentFilter *recent_filter = NULL;
  const gchar *app_name = g_get_application_name ();
  GtkActionGroup *action_group = NULL;
  GtkAccelGroup  *accel_group = NULL;
  GtkWidget *box = NULL;

  /* Set private structure */
  win->priv = priv;

  /* Initialize settings */
  e6x_window_load_settings ();
  priv->fullscreen = FALSE;
  
  /* Main window */
  screen = gtk_window_get_screen (GTK_WINDOW (win));
  gtk_window_set_default_size (GTK_WINDOW (win),
                               gdk_screen_get_width (screen) / 1.5,
                               gdk_screen_get_height (screen) / 1.5);
  gtk_window_set_type_hint (GTK_WINDOW (win), GDK_WINDOW_TOPLEVEL);
  
  /* Paned widget */
  priv->paned = gtk_hpaned_new ();
  
  priv->pane1 = NULL;
  priv->tocview = NULL;
  
  priv->pane2 = gtk_scrolled_window_new (NULL, NULL);
  gtk_paned_add2 (GTK_PANED (priv->paned), priv->pane2);
  gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (priv->pane2),
                                  GTK_POLICY_AUTOMATIC, 
                                  GTK_POLICY_AUTOMATIC);
  GTK_SCROLLED_WINDOW_GET_CLASS (priv->pane2)->scroll_child = NULL;
  GTK_WIDGET_GET_CLASS (priv->pane2)->scroll_event = NULL;
  g_signal_connect (priv->pane2, "size-allocate",
                    G_CALLBACK (e6x_window_pane_size_changed), win);

  priv->view = e6x_view_new ();
  gtk_container_add (GTK_CONTAINER (priv->pane2), priv->view);
  gtk_widget_show_all (priv->paned);
    
  /* Notebook */
  priv->notebook = gtk_notebook_new ();
  gtk_notebook_append_page (GTK_NOTEBOOK (priv->notebook), 
                            priv->paned, NULL);
  gtk_notebook_set_tab_reorderable (GTK_NOTEBOOK (priv->notebook), 
                                    priv->paned, TRUE);
  g_object_set (G_OBJECT (priv->notebook),
                "enable-popup", FALSE,
                "scrollable", TRUE,
                "show-tabs", FALSE,
                "show-border", FALSE,
                NULL);
  g_signal_connect (GTK_NOTEBOOK (priv->notebook), "switch-page",
                    G_CALLBACK (e6x_window_tab_switched), win);
  g_signal_connect (GTK_NOTEBOOK (priv->notebook), "page-added",
                    G_CALLBACK (e6x_window_n_tabs_changed), win);
  g_signal_connect (GTK_NOTEBOOK (priv->notebook), "page-removed",
                    G_CALLBACK (e6x_window_n_tabs_changed), win);


  /* Button to open file in new tab */
  GtkWidget *button = NULL;
  GtkWidget *image = NULL;
  
  button = gtk_button_new ();
  gtk_button_set_relief (GTK_BUTTON (button), GTK_RELIEF_NONE);
  g_signal_connect (GTK_WIDGET (button), "clicked",
                    G_CALLBACK (e6x_window_open),
                    E6X_WINDOW (win));
  gtk_widget_set_tooltip_text (button, _("Open document"));
  image = gtk_image_new_from_stock (GTK_STOCK_OPEN, GTK_ICON_SIZE_MENU);
  gtk_container_add (GTK_CONTAINER (button), image);
  gtk_widget_show_all (button);
  gtk_notebook_set_action_widget (GTK_NOTEBOOK (priv->notebook), 
                                  button, GTK_PACK_END); 
  gtk_widget_set_can_focus (button, FALSE);


  /* Actions */
  action_group = gtk_action_group_new ("actions");
#ifdef ENABLE_NLS
  gtk_action_group_set_translation_domain (action_group, GETTEXT_PACKAGE);
#endif
  gtk_action_group_add_actions (action_group, action_entries,
                                G_N_ELEMENTS (action_entries),
                                win);
  gtk_action_group_add_toggle_actions (action_group, toggle_action_entries,
                                       G_N_ELEMENTS (toggle_action_entries),
                                       win);

  action = gtk_recent_action_new ("recent-files", _("Open _Recent"), NULL, NULL);
  recent_filter = gtk_recent_filter_new ();
  gtk_recent_filter_add_application (recent_filter, app_name);
  gtk_recent_chooser_add_filter (GTK_RECENT_CHOOSER (action), recent_filter);
  gtk_recent_chooser_set_sort_type (GTK_RECENT_CHOOSER (action), 
                                    GTK_RECENT_SORT_MRU);
  g_signal_connect (GTK_RECENT_CHOOSER (action), "item-activated",
                    G_CALLBACK (e6x_window_open_recent), win);
  gtk_action_group_add_action (action_group, action);  
  
  /* UI Manager */
  priv->ui_manager = gtk_ui_manager_new ();
  gtk_ui_manager_insert_action_group (priv->ui_manager, action_group, 0);
  gtk_ui_manager_add_ui_from_string (priv->ui_manager, ui_markup, -1, NULL);
  gtk_ui_manager_ensure_update (priv->ui_manager);

  accel_group = gtk_ui_manager_get_accel_group (priv->ui_manager);
  gtk_window_add_accel_group (GTK_WINDOW (win), accel_group);

  /* Menubar */
  priv->menubar = gtk_ui_manager_get_widget (priv->ui_manager, 
                                             "/main-menu");
  
  /* Right-click menu */
  priv->right_click_menu = gtk_ui_manager_get_widget (priv->ui_manager, 
                                                      "/right-click-menu");
  g_signal_connect_swapped (priv->notebook, 
                            "button-press-event",
                            G_CALLBACK (e6x_window_button_press_event), 
                            priv->right_click_menu);

  /* Toolbar */
  priv->toolbar = gtk_toolbar_new ();
  gtk_widget_set_name (priv->toolbar, "electrix-toolbar");
  gtk_toolbar_set_style (GTK_TOOLBAR (priv->toolbar), GTK_TOOLBAR_ICONS);
                         
  toolitem = gtk_separator_tool_item_new ();
  gtk_tool_item_set_expand (toolitem, TRUE);
  gtk_separator_tool_item_set_draw (GTK_SEPARATOR_TOOL_ITEM (toolitem), FALSE);
  gtk_toolbar_insert (GTK_TOOLBAR (priv->toolbar), toolitem, -1);
  
  toolitem = gtk_tool_item_new ();
  priv->page_selector = e6x_page_selector_new ();
  gtk_container_add (GTK_CONTAINER (toolitem), priv->page_selector);
  gtk_toolbar_insert (GTK_TOOLBAR (priv->toolbar), toolitem, -1);

  toolitem = gtk_separator_tool_item_new ();
  gtk_toolbar_insert (GTK_TOOLBAR (priv->toolbar), toolitem, -1);

  action = gtk_action_group_get_action (action_group, "history-back");
  toolitem = GTK_TOOL_ITEM (gtk_action_create_tool_item (action));
  gtk_toolbar_insert (GTK_TOOLBAR (priv->toolbar), toolitem, -1);
  
  action = gtk_action_group_get_action (action_group, "history-forward");
  toolitem = GTK_TOOL_ITEM (gtk_action_create_tool_item (action));
  gtk_toolbar_insert (GTK_TOOLBAR (priv->toolbar), toolitem, -1);
  
  toolitem = gtk_separator_tool_item_new ();
  gtk_tool_item_set_expand (toolitem, TRUE);
  gtk_separator_tool_item_set_draw (GTK_SEPARATOR_TOOL_ITEM (toolitem), FALSE);
  gtk_toolbar_insert (GTK_TOOLBAR (priv->toolbar), toolitem, -1);

  toolitem = gtk_tool_item_new ();
  priv->scale_selector = e6x_scale_selector_new ();
  gtk_container_add (GTK_CONTAINER (toolitem), priv->scale_selector);
  gtk_toolbar_insert (GTK_TOOLBAR (priv->toolbar), toolitem, -1);

  toolitem = gtk_separator_tool_item_new ();
  gtk_tool_item_set_expand (toolitem, TRUE);
  gtk_separator_tool_item_set_draw (GTK_SEPARATOR_TOOL_ITEM (toolitem), FALSE);
  gtk_toolbar_insert (GTK_TOOLBAR (priv->toolbar), toolitem, -1);

  /* Findbar */
  priv->findbar = gtk_toolbar_new ();
  gtk_toolbar_set_style (GTK_TOOLBAR (priv->findbar), GTK_TOOLBAR_BOTH_HORIZ);
  gtk_toolbar_set_icon_size (GTK_TOOLBAR (priv->findbar), GTK_ICON_SIZE_MENU);
  g_signal_connect (priv->findbar, "key-press-event",
                    G_CALLBACK (e6x_window_find_key_press_event), NULL);
  
  toolitem = gtk_tool_item_new ();
  gtk_container_set_border_width (GTK_CONTAINER (toolitem), 5);
  GtkWidget *label = gtk_label_new (_("Find:"));
  gtk_container_add (GTK_CONTAINER (toolitem), label);
  gtk_toolbar_insert (GTK_TOOLBAR (priv->findbar), toolitem, -1);
  
  action = gtk_action_group_get_action (action_group, "find-prev");
  toolitem = GTK_TOOL_ITEM (gtk_action_create_tool_item (action));
  gtk_toolbar_insert (GTK_TOOLBAR (priv->findbar), toolitem, -1);
  
  toolitem = gtk_tool_item_new ();
  priv->find_entry = gtk_entry_new ();
  gtk_container_add (GTK_CONTAINER (toolitem), priv->find_entry);
  gtk_toolbar_insert (GTK_TOOLBAR (priv->findbar), toolitem, -1);
  g_signal_connect (priv->find_entry, "activate",
                    G_CALLBACK (e6x_window_find_activate), win);
  
  action = gtk_action_group_get_action (action_group, "find-next");
  toolitem = GTK_TOOL_ITEM (gtk_action_create_tool_item (action));
  gtk_toolbar_insert (GTK_TOOLBAR (priv->findbar), toolitem, -1);

  toolitem = gtk_tool_item_new();
  gtk_tool_item_set_expand (toolitem, TRUE);
  gtk_container_set_border_width (GTK_CONTAINER (toolitem), 5);
  priv->find_status_label = gtk_label_new (NULL);
  gtk_label_set_ellipsize (GTK_LABEL (priv->find_status_label),
                           PANGO_ELLIPSIZE_END);
  gtk_misc_set_alignment (GTK_MISC (priv->find_status_label), 0.0, 0.5);
  gtk_container_add (GTK_CONTAINER (toolitem), priv->find_status_label);
  gtk_toolbar_insert (GTK_TOOLBAR (priv->findbar), toolitem, -1);

  toolitem = gtk_tool_button_new_from_stock (GTK_STOCK_CLOSE);
  g_signal_connect (toolitem, "clicked",
                    G_CALLBACK (e6x_window_hide_findbar), priv->findbar);
  gtk_toolbar_insert (GTK_TOOLBAR (priv->findbar), toolitem, -1);


  /* Box with the main widgets */
  box = gtk_vbox_new (FALSE, 0);
  gtk_container_add (GTK_CONTAINER (win), box);
  gtk_box_pack_start (GTK_BOX (box), priv->menubar, FALSE, FALSE, 0);
  gtk_box_pack_start (GTK_BOX (box), priv->notebook, TRUE, TRUE, 0);  
  gtk_box_pack_start (GTK_BOX (box), priv->findbar, FALSE, FALSE, 0);
  gtk_box_pack_start (GTK_BOX (box), priv->toolbar, FALSE, FALSE, 0);
  
  /* Drag and drop */
  gtk_drag_dest_set (GTK_WIDGET (win), GTK_DEST_DEFAULT_ALL,
                     NULL, 0, GDK_ACTION_COPY|GDK_ACTION_MOVE);
  gtk_drag_dest_add_uri_targets (GTK_WIDGET (win));
  g_signal_connect (GTK_WIDGET (win), "drag-data-received",
                    G_CALLBACK (e6x_window_uri_dropped), NULL);
  
  /* Show default widgets */  
  gtk_widget_show_all (priv->menubar);
  if (e6x_window_settings.show_toolbar)
    gtk_widget_show_all (priv->toolbar);
  gtk_widget_show (priv->notebook);
  gtk_widget_show (box);
  gtk_widget_show (GTK_WIDGET (win));

  e6x_window_update_ui (NULL, win);

  g_object_unref (accel_group);
  g_object_unref (action_group);
}


static void
e6x_window_finalize (GObject *object)
{
  GList *windows = NULL;
  
  windows = e6x_window_list ();
  
  if (windows == NULL && gtk_main_level () != 0)
    gtk_main_quit ();
  
  g_list_free (windows);
  
  G_OBJECT_CLASS (e6x_window_parent_class)->finalize (object);
}


static void
e6x_window_dispose (GObject *object)
{
  E6xWindowPrivate *priv = E6X_WINDOW_GET_PRIVATE (object);
  
  e6x_window_set_document (E6X_WINDOW (object), NULL);  
  if (priv->ui_manager != NULL) 
  {
    g_object_unref (priv->ui_manager);
    priv->ui_manager = NULL;
  }
  priv->notebook = NULL;
  priv->paned = NULL;
  priv->pane1 = NULL;
  priv->pane2 = NULL;
  priv->tocview = NULL;
  priv->view = NULL;
  priv->menubar = NULL;
  priv->right_click_menu = NULL;
  priv->toolbar = NULL;
  priv->findbar = NULL;
  priv->find_entry = NULL;
  priv->find_status_label = NULL;
  priv->page_selector = NULL;
  priv->scale_selector = NULL;
  
  G_OBJECT_CLASS (e6x_window_parent_class)->dispose (object);
}


static void
e6x_window_open (GtkAction *action, 
                 E6xWindow *win)
{
  E6xDocument *doc = NULL;

  doc = e6x_dialogs_open_file (GTK_WINDOW (win));
  
  if (G_LIKELY (doc != NULL))
  {
    e6x_window_add_document (win, doc);
    g_object_unref (doc);
  }
}


static void 
e6x_window_open_recent (GtkRecentChooser *chooser,
                        E6xWindow *win)
{
  gchar *uri = NULL;
  E6xDocument *doc = NULL;
  
  uri = gtk_recent_chooser_get_current_uri (chooser);
  doc = e6x_util_open_document (uri, NULL, NULL);
  g_free (uri);
  
  if (G_LIKELY (doc != NULL))
  {
    e6x_window_add_document (win, doc);
    g_object_unref (doc);
  }
}


static void 
e6x_window_open_in_new_window (GtkAction *action, 
                               E6xWindow *win)
{
  E6xDocument *doc = NULL;
  GtkWidget *new_win = NULL;

  doc = e6x_dialogs_open_file (NULL);
  
  if (G_LIKELY (doc != NULL))
  {
    new_win = e6x_window_new ();
    e6x_window_add_document (E6X_WINDOW (new_win), doc);
    g_object_unref (doc);
  }
}


static void
e6x_window_reload (GtkAction *action, 
                   E6xWindow *win)
{
  E6xDocument *doc = win->priv->doc;
  
  g_return_if_fail (doc != NULL);
  e6x_document_reload (doc, NULL);
  e6x_window_update_toc_view (win);
}


static void
e6x_window_decrypt (GtkAction *action, 
                    E6xWindow *win)
{
  E6xWindowPrivate *priv = win->priv;
  E6xDocument *doc = NULL;
  
  g_return_if_fail (priv->doc != NULL);
  
  doc = e6x_dialogs_decrypt (priv->doc, GTK_WINDOW (win));  

  if (G_LIKELY (doc != NULL))
  {
    e6x_window_set_document (win, doc);
    g_object_unref (doc);
  }
}


static void
e6x_window_save_copy (GtkAction *action, 
                      E6xWindow *win)
{
  g_return_if_fail (win->priv->doc != NULL);
  e6x_dialogs_save_copy (win->priv->doc, GTK_WINDOW (win));  
}


static void
e6x_window_print (GtkAction *action, 
                  E6xWindow *win)
{
  e6x_dialogs_print (win->priv->doc, GTK_WINDOW (win));
}


static void 
e6x_window_show_properties (GtkAction *action, 
                            E6xWindow *win)
{
  e6x_dialogs_properties (win->priv->doc, GTK_WINDOW (win));
}


static void 
e6x_window_show_attachments (GtkAction *action, 
                             E6xWindow *win)
{
  E6xDocument *doc = win->priv->doc;
  GtkWidget *view = NULL;
  GtkWidget *scrolled = NULL;
  GtkWidget *attwin = NULL;
  gchar *title = NULL;  
  
  g_return_if_fail (doc != NULL && doc->attlist != NULL);
  
  attwin = gtk_window_new (GTK_WINDOW_TOPLEVEL);
  gtk_window_set_type_hint (GTK_WINDOW (attwin), 
                            GDK_WINDOW_TYPE_HINT_UTILITY);
  gtk_window_set_default_size (GTK_WINDOW (attwin), 640, 400);
  title = g_strdup_printf ("%s - %s - %s", _("Attachments"), 
                           doc->title, g_get_application_name ());
  gtk_window_set_title (GTK_WINDOW (attwin), title);

  scrolled = gtk_scrolled_window_new (NULL, NULL);
  gtk_container_add (GTK_CONTAINER (attwin), scrolled);
  gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled),
                                  GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);

  view = e6x_att_view_new (doc);
  gtk_container_add (GTK_CONTAINER (scrolled), view);  

  gtk_widget_show_all (attwin);
  
  g_free (title);
}


static void 
e6x_window_close_tab (GtkAction *action, 
                      E6xWindow *win)
{
  E6xWindowPrivate *priv = win->priv;
  guint tab_num;
  
  tab_num = gtk_notebook_get_current_page (GTK_NOTEBOOK (priv->notebook));
  gtk_notebook_remove_page (GTK_NOTEBOOK (priv->notebook), tab_num);
}


static void 
e6x_window_close_window (GtkAction *action, 
                         E6xWindow *win)
{
  gtk_widget_destroy (GTK_WIDGET (win));
}


static void
e6x_window_quit (GtkAction *action, 
                 E6xWindow *win)
{
  gtk_main_quit ();
}


static void 
e6x_window_select_text (GtkAction *action,
                        E6xWindow *win)
{
  e6x_view_set_select_mode (E6X_VIEW (win->priv->view), TRUE);
}


static void 
e6x_window_find (GtkAction *action,
                 E6xWindow *win)
{
  E6xWindowPrivate *priv = win->priv;
  const gchar *action_name = gtk_action_get_name (action);
  
  gtk_widget_show_all (priv->findbar);
  gtk_widget_grab_focus (priv->find_entry);
  gtk_editable_select_region (GTK_EDITABLE (priv->find_entry), 0, -1);
  
  if (g_strcmp0 (action_name, "find-prev") == 0)
    e6x_window_find_iterate (win, BACKWARD);
  else if (g_strcmp0 (action_name, "find-next") == 0)
    e6x_window_find_iterate (win, FORWARD);
}


static void
e6x_window_zoom (GtkAction *action, 
                 E6xWindow *win)
{
  E6xWindowPrivate *priv = win->priv;
  const gchar *action_name = gtk_action_get_name (action);
  gdouble new_scale = priv->doc->scale;
  
  if (G_UNLIKELY (priv->doc == NULL))
    return;
  
  if (g_strcmp0 (action_name, "zoom-in") == 0)
    new_scale = priv->doc->scale + e6x_window_settings.zoom_step;
  else if (g_strcmp0 (action_name, "zoom-out") == 0)
    new_scale = priv->doc->scale - e6x_window_settings.zoom_step;
  else if (g_strcmp0 (action_name, "zoom-reset") == 0)
    new_scale = e6x_window_settings.zoom_default;
  else
    g_warn_if_reached ();
  
  new_scale = CLAMP (new_scale, 
                     e6x_window_settings.zoom_min, 
                     e6x_window_settings.zoom_max);
  e6x_document_set_scale (priv->doc, new_scale);
}


static void
e6x_window_rotate (GtkAction *action, 
                   E6xWindow *win)
{
  E6xWindowPrivate *priv = win->priv;
  const gchar *action_name = gtk_action_get_name (action);
  gint new_angle = 0;
  
  if (G_UNLIKELY (priv->doc == NULL))
    return;
  
  if (g_strcmp0 (action_name, "rotate-cw") == 0)
    new_angle = MODULO (priv->doc->angle + 90, 360);
  else if (g_strcmp0 (action_name, "rotate-ccw") == 0)
    new_angle = MODULO (priv->doc->angle - 90, 360);
  else
    g_warn_if_reached ();
  
  e6x_document_set_angle (priv->doc, new_angle);
}


static void 
e6x_window_toggle_color (GtkAction *action,
                         E6xWindow *win)
{
  gboolean inversion = FALSE;
  
  if (G_UNLIKELY (win->priv->doc == NULL))
    return;
  
  if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
    inversion = TRUE;
  
  e6x_document_set_color_inversion (win->priv->doc, inversion);
}


static void
e6x_window_toggle_fit (GtkAction *action, 
                       E6xWindow *win)
{
  E6xWindowPrivate *priv = win->priv;
  const gchar *action_name = gtk_action_get_name (action);
  GtkAllocation pane_alloc = {0, 0, 0, 0};
  GtkWidget *sbar = NULL;
  GtkAllocation sbar_alloc = {0, 0, 0, 0};
  gint spacing = 3;
  gdouble fit_width = 0.0, fit_height = 0.0;
  
  if (G_UNLIKELY (priv->doc == NULL))
    return;

  gtk_widget_get_allocation (priv->pane2, &pane_alloc);
  sbar = gtk_scrolled_window_get_vscrollbar (GTK_SCROLLED_WINDOW (priv->pane2));
  gtk_widget_get_allocation (sbar, &sbar_alloc);
  gtk_widget_style_get (priv->pane2, "scrollbar-spacing", &spacing, NULL);
  
  fit_width = priv->doc->fit_width;
  fit_height = priv->doc->fit_height;
  
  if (g_strcmp0 (action_name, "fit-page") == 0)
  {
    if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
    {
      fit_width = pane_alloc.width;
      fit_height = pane_alloc.height;
    }
    else if (fit_width > 0 && fit_height > 0)
    {
      fit_width = 0.0;
      fit_height = 0.0;
    }
  }
  else if (g_strcmp0 (action_name, "fit-width") == 0)
  {
    if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
    {
      fit_width = pane_alloc.width - sbar_alloc.width - spacing;
      fit_height = 0.0;
    }
    else if (fit_width > 0 && fit_height == 0)
    {
      fit_width = 0.0;
    }
  }
  else
    g_warn_if_reached ();

  e6x_document_set_fit (priv->doc, fit_width, fit_height);
}


static void
e6x_window_toggle_fullscreen (GtkAction *action, 
                              E6xWindow *win)
{
  E6xWindowPrivate *priv = win->priv;
  
  if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
  {
    gtk_widget_hide (priv->menubar);
    gtk_widget_hide (priv->toolbar);
    gtk_window_fullscreen (GTK_WINDOW (win));
    priv->fullscreen = TRUE;
  }
  else
  { 
    if (e6x_window_settings.show_toolbar)
      gtk_widget_show_all (priv->toolbar);
    gtk_widget_show (priv->menubar);
    gtk_window_unfullscreen (GTK_WINDOW (win));
    priv->fullscreen = FALSE;
  }
  e6x_window_toggle_show_tabs (win);
}


static void 
e6x_window_toggle_toolbar (GtkAction *action, 
                           E6xWindow *win)
{
  if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
  {
    gtk_widget_show_all (win->priv->toolbar);    
    e6x_window_settings.show_toolbar = TRUE;
  }
  else
  {
    gtk_widget_hide (win->priv->toolbar);
    e6x_window_settings.show_toolbar = FALSE;
  }
}


static void 
e6x_window_toggle_toc (GtkAction *action, 
                       E6xWindow *win)
{
  E6xWindowPrivate *priv = win->priv;
  
  if (G_UNLIKELY (priv->doc == NULL))
    return;
  
  if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
  {
    if (win->priv->pane1 != NULL)
      gtk_widget_show_all (win->priv->pane1);
    e6x_document_set_show_toc (priv->doc, TRUE);
  }
  else
  {
    if (win->priv->pane1 != NULL)
      gtk_widget_hide (win->priv->pane1);
    e6x_document_set_show_toc (priv->doc, FALSE);
  }
}


static void
e6x_window_switch_page (GtkAction *action, 
                        E6xWindow *win)
{
  E6xWindowPrivate *priv = win->priv;
  const gchar *action_name = gtk_action_get_name (action);
  
  if (G_UNLIKELY (priv->doc == NULL))
    return;
  
  if (g_strcmp0 (action_name, "prev") == 0)
    e6x_document_set_page_no (priv->doc, priv->doc->page_no - 1);
  else if (g_strcmp0 (action_name, "next") == 0)
    e6x_document_set_page_no (priv->doc, priv->doc->page_no + 1);
  else if (g_strcmp0 (action_name, "first") == 0)
    e6x_document_set_page_no (priv->doc, 1);
  else if (g_strcmp0 (action_name, "last") == 0)
    e6x_document_set_page_no (priv->doc, priv->doc->n_pages);
  else
    g_warn_if_reached ();
}


static void
e6x_window_about (GtkAction *action, 
                  E6xWindow *win)
{
  e6x_dialogs_about (GTK_WINDOW (win));
}


static void
e6x_window_switch_tab (GtkAction *action, 
                       E6xWindow *win)
{
  const gchar *action_name = gtk_action_get_name (action);
  
  if (g_strcmp0 (action_name, "tab-prev") == 0)
    gtk_notebook_prev_page (GTK_NOTEBOOK (win->priv->notebook));
  else if (g_strcmp0 (action_name, "tab-next") == 0)
    gtk_notebook_next_page (GTK_NOTEBOOK (win->priv->notebook));
  else
    g_warn_if_reached ();
}


static void 
e6x_window_walk_history (GtkAction *action, 
                         E6xWindow *win)
{
  E6xWindowPrivate *priv = win->priv;
  const gchar *action_name = gtk_action_get_name (action);
  
  if (g_strcmp0 (action_name, "history-back") == 0)
    e6x_view_walk_history (E6X_VIEW (priv->view), BACKWARD);
  else if (g_strcmp0 (action_name, "history-forward") == 0)
    e6x_view_walk_history (E6X_VIEW (priv->view), FORWARD);
  else
    g_warn_if_reached ();
}


static void 
e6x_window_uri_dropped (GtkWidget *widget,
                        GdkDragContext *drag_context,
                        gint x,
                        gint y,
                        GtkSelectionData *data,
                        guint info,
                        guint time)
{
  gchar **uris = NULL;
  
  if ((gtk_selection_data_get_length (data) >= 0) && 
      (gtk_selection_data_get_format (data) == 8))
    uris = gtk_selection_data_get_uris (data);

  if (uris != NULL)
  {
    gchar **p = NULL;
    
    for (p = uris; *p != NULL; p++)
    {
      E6xDocument *doc = NULL;
      
      doc = e6x_util_open_document (*p, NULL, NULL);
      if (doc != NULL)
      {
        e6x_window_add_document (E6X_WINDOW (widget), doc);
        g_object_unref (doc);
      }
    }
    
    g_strfreev (uris);
    gtk_drag_finish (drag_context, TRUE, FALSE, time);
    return;
  }
  
  gtk_drag_finish (drag_context, FALSE, FALSE, time);
}


static void  
e6x_window_close_button_clicked (GtkButton *button, 
                                 GtkWidget *child)
{
  GtkWidget *notebook = NULL;
  guint tab_num = 0;
  
  notebook = gtk_widget_get_ancestor (child, GTK_TYPE_NOTEBOOK);
  if (G_UNLIKELY (notebook == NULL))
    return;
  tab_num = gtk_notebook_page_num (GTK_NOTEBOOK (notebook), child);
  gtk_notebook_remove_page (GTK_NOTEBOOK (notebook), tab_num);  
}


static void 
e6x_window_find_activate (GtkEntry *entry, 
                          E6xWindow *win)
{
  e6x_window_find_iterate (win, FORWARD);
}


static gboolean
e6x_window_find_key_press_event (GtkWidget *bar,
                                 GdkEventKey *event)
{
  if (event->keyval == GDK_Escape)
  {
    e6x_window_hide_findbar (NULL, bar);
    return TRUE;
  }
  else
    return FALSE;
}


static void 
e6x_window_hide_findbar (GtkToolButton *button, 
                         GtkWidget *bar)
{
  if (gtk_widget_get_visible (bar))
    gtk_widget_hide (bar);
}


static void 
e6x_window_tab_switched (GtkNotebook *notebook,
                         gpointer *tab,
                         guint tab_num,
                         E6xWindow *win)
{
  E6xWindowPrivate *priv = win->priv;
  
  priv->paned = gtk_notebook_get_nth_page (GTK_NOTEBOOK (priv->notebook), 
                                           tab_num);
  
  priv->pane1 = gtk_paned_get_child1 (GTK_PANED (priv->paned));
  if (priv->pane1 != NULL)
    priv->tocview = gtk_bin_get_child (GTK_BIN (priv->pane1));
  else
    priv->tocview = NULL;
  
  if (priv->pane2 != NULL)
    g_signal_handlers_disconnect_by_func (priv->pane2,
                                          e6x_window_pane_size_changed,
                                          win);
  priv->pane2 = gtk_paned_get_child2 (GTK_PANED (priv->paned));
  g_signal_connect (priv->pane2, "size-allocate",
                    G_CALLBACK (e6x_window_pane_size_changed), win);
  
  priv->view = gtk_bin_get_child (GTK_BIN (priv->pane2));
  e6x_window_set_document (win, e6x_view_get_document (E6X_VIEW (priv->view)));
  
  e6x_window_update_fit (win);
}


static void 
e6x_window_n_tabs_changed (GtkNotebook *notebook,
                           gpointer *tab,
                           guint tab_num,
                           E6xWindow *win)
{
  E6xWindowPrivate *priv = win->priv;
  
  if (G_UNLIKELY (priv->notebook == NULL))
      return;
  
  if (gtk_notebook_get_n_pages (GTK_NOTEBOOK (priv->notebook)) == 0)
  {
    gtk_widget_destroy (GTK_WIDGET (win));
    return;
  }
  
  e6x_window_toggle_show_tabs (win);
}


static void 
e6x_window_pane_size_changed (GtkWidget *pane,
                              GdkRectangle *allocation,
                              E6xWindow *win)
{
  static gint old_width = 0, old_height = 0;

  if (G_UNLIKELY (!gtk_widget_get_realized (pane)))
    return;
    
  if (allocation->width != old_width ||
      allocation->height != old_height)
    e6x_window_update_fit (win);
  
  old_width = allocation->width;
  old_height = allocation->height;
}


static void
e6x_window_update_ui (E6xDocument *doc, 
                      E6xWindow *win)
{
  E6xWindowPrivate *priv = win->priv;
  E6xView *view = E6X_VIEW (priv->view);
  E6xDocumentClass *doc_class = NULL;
  GtkAction *action = NULL;
  
  if (doc != NULL)
    doc_class = E6X_DOCUMENT_GET_CLASS (doc);
  
  action = gtk_ui_manager_get_action (priv->ui_manager, "/decrypt");
  gtk_action_set_sensitive (action, E6X_IS_ENC_DOCUMENT (doc));
  
  action = gtk_ui_manager_get_action (priv->ui_manager, "/save-copy");
  gtk_action_set_sensitive (action, 
                            doc != NULL && doc_class->save_copy != NULL);

  action = gtk_ui_manager_get_action (priv->ui_manager, "/print");
  gtk_action_set_sensitive (action, doc != NULL);
  
  action = gtk_ui_manager_get_action (priv->ui_manager, "/show-properties");
  gtk_action_set_sensitive (action, doc != NULL && doc->info != NULL);

  action = gtk_ui_manager_get_action (priv->ui_manager, "/show-attachments");
  gtk_action_set_sensitive (action, doc != NULL && doc->attlist != NULL);

  action = gtk_ui_manager_get_action (priv->ui_manager, "/select-text");
  gtk_action_set_sensitive (action, 
                            doc != NULL && doc_class->get_text != NULL);
  
  action = gtk_ui_manager_get_action (priv->ui_manager, "/find-text");
  gtk_action_set_sensitive (action, 
                            doc != NULL && doc_class->get_n_matches != NULL);

  action = gtk_ui_manager_get_action (priv->ui_manager, "/find-prev");
  gtk_action_set_sensitive (action, 
                            doc != NULL && doc_class->get_n_matches != NULL);

  action = gtk_ui_manager_get_action (priv->ui_manager, "/find-next");
  gtk_action_set_sensitive (action, 
                            doc != NULL && doc_class->get_n_matches != NULL);

  action = gtk_ui_manager_get_action (priv->ui_manager, "/reload");
  gtk_action_set_sensitive (action, doc != NULL);

  action = gtk_ui_manager_get_action (priv->ui_manager, "/fit-page");
  gtk_action_set_sensitive (action, doc != NULL);
  if (doc != NULL)
  {
    gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action),
                                  doc->fit_width > 0 && doc->fit_height > 0);
  }

  action = gtk_ui_manager_get_action (priv->ui_manager, "/fit-width");
  gtk_action_set_sensitive (action, doc != NULL);
  if (doc != NULL)
  {
    gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action),
                                  doc->fit_width > 0 && doc->fit_height == 0);
  }

  action = gtk_ui_manager_get_action (priv->ui_manager, "/zoom-in");
  gtk_action_set_sensitive (action, 
                            doc != NULL && doc->scale < e6x_window_settings.zoom_max);

  action = gtk_ui_manager_get_action (priv->ui_manager, "/zoom-out");
  gtk_action_set_sensitive (action, 
                            doc != NULL && doc->scale > e6x_window_settings.zoom_min);

  action = gtk_ui_manager_get_action (priv->ui_manager, "/zoom-reset");
  gtk_action_set_sensitive (action, 
                            doc != NULL && doc->scale != e6x_window_settings.zoom_default);

  action = gtk_ui_manager_get_action (priv->ui_manager, "/rotate-cw");
  gtk_action_set_sensitive (action, doc != NULL);

  action = gtk_ui_manager_get_action (priv->ui_manager, "/rotate-ccw");
  gtk_action_set_sensitive (action, doc != NULL);

  action = gtk_ui_manager_get_action (priv->ui_manager, "/color-inversion");
  gtk_action_set_sensitive (action, doc != NULL);
  if (doc != NULL)
  {
    gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action),
                                  doc->color_inversion);
  }
  
  action = gtk_ui_manager_get_action (priv->ui_manager, "/show-toc");
  gtk_action_set_sensitive (action, doc != NULL && priv->pane1 != NULL);
  if (doc != NULL)
  {
    gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action),
                                  doc->show_toc);
  }
  
  action = gtk_ui_manager_get_action (priv->ui_manager, "/show-toolbar");
  gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action),
                                e6x_window_settings.show_toolbar);
  
  action = gtk_ui_manager_get_action (priv->ui_manager, "/fullscreen");
  gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action),
                                priv->fullscreen);
  
  action = gtk_ui_manager_get_action (priv->ui_manager, "/prev");
  gtk_action_set_sensitive (action, 
                            doc != NULL && doc->page_no > 1);

  action = gtk_ui_manager_get_action (priv->ui_manager, "/next");
  gtk_action_set_sensitive (action, 
                            doc != NULL && doc->page_no < doc->n_pages);

  action = gtk_ui_manager_get_action (priv->ui_manager, "/first");
  gtk_action_set_sensitive (action, 
                            doc != NULL && doc->page_no > 1);

  action = gtk_ui_manager_get_action (priv->ui_manager, "/last");
  gtk_action_set_sensitive (action, 
                            doc != NULL && doc->page_no < doc->n_pages);
  
  action = gtk_ui_manager_get_action (priv->ui_manager, "/history-back");
  gtk_action_set_sensitive (action, e6x_view_has_history (view, BACKWARD));
  
  action = gtk_ui_manager_get_action (priv->ui_manager, "/history-forward");
  gtk_action_set_sensitive (action, e6x_view_has_history (view, FORWARD));
}


static gboolean 
e6x_window_button_press_event (GtkWidget *widget,
                               GdkEventButton *event)
{
  g_return_val_if_fail (GTK_IS_MENU (widget), FALSE);
  
  if (event->button == 3)
  {
    gtk_menu_popup (GTK_MENU (widget), NULL, NULL, NULL, NULL,
                    event->button, event->time);
    return TRUE;
  }
  
  return FALSE;
}


static GtkWidget *
e6x_window_new_tab (E6xWindow *win)
{
  E6xWindowPrivate *priv = win->priv;
  GtkWidget *paned = NULL, *pane2 = NULL, *view =NULL;
  guint tab_num;

  paned = gtk_hpaned_new ();
  
  pane2 = gtk_scrolled_window_new (NULL, NULL);
  gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (pane2),
                                  GTK_POLICY_AUTOMATIC, 
                                  GTK_POLICY_AUTOMATIC);

  gtk_paned_add2 (GTK_PANED (paned), pane2);
  
  view = e6x_view_new ();
  gtk_container_add (GTK_CONTAINER (pane2), view);
  
  gtk_widget_show_all (paned);
  
  tab_num = gtk_notebook_append_page (GTK_NOTEBOOK (priv->notebook), 
                                      paned, NULL);
  gtk_notebook_set_tab_reorderable (GTK_NOTEBOOK (priv->notebook), 
                                    paned, TRUE);
  gtk_notebook_set_current_page (GTK_NOTEBOOK (priv->notebook), tab_num);
  
  return paned;
}


static void
e6x_window_add_recent (const gchar *filename)
{
  gchar *uri = NULL;

  uri = g_filename_to_uri (filename, NULL, NULL);
  g_return_if_fail (uri != NULL);

  gtk_recent_manager_add_item (gtk_recent_manager_get_default (), uri);
  
  g_free (uri);
}


static void 
e6x_window_find_iterate (E6xWindow *win,
                         E6xDirection dir)
{
  static gchar *string = NULL;
  static guint n_matches = 0;
  static gint match_no = 0;

  E6xWindowPrivate *priv = win->priv;
  const gchar *new_string = gtk_entry_get_text (GTK_ENTRY (priv->find_entry));
  gint page_no = 0;
  gchar *status_text = NULL;
  
  g_return_if_fail (priv->doc != NULL);
  page_no = priv->doc->page_no;
 
  if (g_strcmp0 (string, new_string) != 0)
  {
    if (string != NULL)
      g_free (string);
    string = g_strdup (new_string);
    match_no = 0;
    n_matches = 0;
  }
  else
  {
    match_no += dir;
    if (match_no == n_matches || match_no == -1)
    {
      page_no = MODULO (page_no - 1 + dir, priv->doc->n_pages) + 1;
      /* Keep match_no = -1 as is, otherwise nullify  */
      match_no = MIN (0, match_no); 
      n_matches = 0;
    }
  }
  
  if (*string == '\0')
    return;
   
  guint i = 0; 
  gboolean halt = FALSE;
  
  for (i = 0; i < priv->doc->n_pages; i++)
  {
    n_matches = e6x_document_get_n_matches (priv->doc, page_no, string);
    
    status_text = g_strdup_printf (_("Found %d occurrence(s) of '%s' on page %d"), 
                                   n_matches, string, page_no);
    gtk_label_set_text (GTK_LABEL (priv->find_status_label), 
                        status_text);
    g_free (status_text);
    while (gtk_events_pending ())
      gtk_main_iteration ();
    
    halt = !gtk_widget_get_visible (priv->findbar);
    
    if (n_matches > 0 || halt)
      break;
    else
      page_no = MODULO (page_no - 1 + dir, priv->doc->n_pages) + 1;
  }
  
  if (halt)
  {
    gtk_label_set_text (GTK_LABEL (priv->find_status_label), 
                        _("Search aborted"));
  }
  else if (n_matches > 0)
  {
    if (page_no != priv->doc->page_no)
      e6x_document_set_page_no (priv->doc, page_no);
    match_no = MODULO (match_no, n_matches);
    e6x_view_show_nth_match (E6X_VIEW (priv->view), string, match_no);
  }
  else
  {
    status_text = g_strdup_printf (_("Found no occurrences of '%s' in the document"), 
                                   string);
    gtk_label_set_text (GTK_LABEL (priv->find_status_label), 
                        status_text);
    g_free (status_text);
  }
}


static GList *
e6x_window_list ()
{
  GList *l = NULL, *toplevels = NULL, *windows = NULL;
  
  toplevels = gtk_window_list_toplevels ();
  
  for (l = toplevels; l; l = l->next) 
  {
    if (E6X_IS_WINDOW (l->data)) 
    {
      windows = g_list_append (windows, l->data);
    }
  }
  
  g_list_free (toplevels);
  
  return windows;
}


static void
e6x_window_load_settings ()
{
  GKeyFile *keyfile = e6x_pref_get_keyfile ();

  e6x_window_settings.zoom_min 
    = g_key_file_get_double (keyfile, "Zoom", "Min", NULL);
  e6x_window_settings.zoom_max 
    = g_key_file_get_double (keyfile, "Zoom", "Max", NULL);
  e6x_window_settings.zoom_step 
    = g_key_file_get_double (keyfile, "Zoom", "Step", NULL);
  e6x_window_settings.zoom_default 
    = g_key_file_get_double (keyfile, "Zoom", "Default", NULL); 
}


static void 
e6x_window_set_title (E6xWindow *win,
                      const gchar *text)
{
  gchar *title = NULL;
    
  title = g_strdup_printf ("%s - %s", text, g_get_application_name ());
  gtk_window_set_title (GTK_WINDOW (win), title);
  g_free (title);
}


static GtkWidget *
e6x_window_create_tab_label (const gchar *text, 
                             GtkWidget *child)
{
  GtkWidget *label = NULL, *button = NULL, *image = NULL;
  GtkWidget *hbox = NULL;
  GtkRequisition size;
  GtkRcStyle *rcstyle = NULL;

  label = gtk_label_new (text);
  gtk_label_set_width_chars (GTK_LABEL (label), 20);
  gtk_label_set_ellipsize (GTK_LABEL (label), PANGO_ELLIPSIZE_END);
  gtk_misc_set_padding (GTK_MISC (label), 0, 0);
  gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);  
  gtk_widget_set_tooltip_text (label, text);

  image = gtk_image_new_from_stock (GTK_STOCK_CLOSE, GTK_ICON_SIZE_MENU);
  gtk_widget_size_request (image, &size);

  button = gtk_button_new ();
  gtk_widget_set_size_request (button, size.width, size.height);
  gtk_button_set_relief (GTK_BUTTON (button), GTK_RELIEF_NONE);
  gtk_button_set_focus_on_click (GTK_BUTTON (button), FALSE);
  gtk_widget_set_tooltip_text (button, _("Close Tab"));
  g_signal_connect (G_OBJECT (button), "clicked",
                    G_CALLBACK (e6x_window_close_button_clicked),
                    child);
  gtk_container_add (GTK_CONTAINER (button), image);
  
  rcstyle = gtk_rc_style_new ();
	rcstyle->xthickness = rcstyle->ythickness = 0;
	gtk_widget_modify_style (button, rcstyle);
	g_object_unref (rcstyle);

  hbox = gtk_hbox_new (FALSE, 0);
  gtk_box_pack_start (GTK_BOX (hbox), label, TRUE, TRUE, 0);
  gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0);
  gtk_widget_show_all (hbox);
  
  return hbox;
}


static void
e6x_window_toggle_show_tabs (E6xWindow *win)
{
  E6xWindowPrivate *priv = win->priv;
  GtkNotebook *notebook = GTK_NOTEBOOK (priv->notebook);
  guint n_tabs = 0;
  
  n_tabs = gtk_notebook_get_n_pages (notebook);
  
  gtk_notebook_set_show_tabs (notebook, n_tabs>1 && !priv->fullscreen);
  gtk_notebook_set_show_border (notebook, n_tabs>1 && !priv->fullscreen);
}


static void 
e6x_window_update_toc_view (E6xWindow *win)
{
  E6xWindowPrivate *priv = win->priv;
  GtkAllocation paned_alloc = {0, 0, 0, 0};
  
  if (priv->doc == NULL || priv->doc->toc == NULL)
  {
    if (priv->pane1 != NULL)
    {
      gtk_widget_destroy (priv->pane1);
      priv->pane1 = NULL;
      priv->tocview = NULL;
    }
    return;
  }
  
  if (priv->pane1 == NULL)
  {
    g_warn_if_fail (priv->tocview == NULL);  
  
    priv->pane1 = gtk_scrolled_window_new (NULL, NULL);
    gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (priv->pane1),
                                    GTK_POLICY_AUTOMATIC, 
                                    GTK_POLICY_AUTOMATIC);
    gtk_paned_add1 (GTK_PANED (priv->paned), priv->pane1);
    
    
    priv->tocview = e6x_toc_view_new (priv->doc);
    gtk_container_add (GTK_CONTAINER (priv->pane1), priv->tocview);
    
    /* Provisional solution to position the divider */  
    gtk_widget_get_allocation (priv->paned, &paned_alloc);
    gtk_paned_set_position (GTK_PANED (priv->paned), paned_alloc.width / 4);
  }
  
  if (priv->doc->show_toc)
    gtk_widget_show_all (priv->pane1);
  else
    gtk_widget_hide (priv->pane1);
}


static void
e6x_window_update_fit (E6xWindow *win)
{
  E6xWindowPrivate *priv = win->priv;
  GtkAllocation pane_alloc = {0, 0, 0, 0};
  GtkWidget *sbar = NULL;
  GtkAllocation sbar_alloc = {0, 0, 0, 0};
  gint spacing = 3;
  gdouble fit_width = 0.0, fit_height = 0.0;
  
  if (G_UNLIKELY (priv->doc == NULL))
    return;
  
  gtk_widget_get_allocation (priv->pane2, &pane_alloc);
  sbar = gtk_scrolled_window_get_vscrollbar (GTK_SCROLLED_WINDOW (priv->pane2));
  gtk_widget_get_allocation (sbar, &sbar_alloc);
  gtk_widget_style_get (priv->pane2, "scrollbar-spacing", &spacing, NULL);
  
  fit_width = priv->doc->fit_width;
  fit_height = priv->doc->fit_height;  
  if (fit_width > 0 && fit_height > 0)
  {
    fit_width = pane_alloc.width;
    fit_height = pane_alloc.height;
  }
  else if (fit_width > 0)
  {
    fit_width = pane_alloc.width - sbar_alloc.width - spacing;
  }
  else
    return;
  
  e6x_document_set_fit (priv->doc, fit_width, fit_height);
}
