/*
 * This file is part of the apvlv package
 *
 * Copyright (C) 2008 Alf.
 *
 * Contact: Alf <naihe2010@126.com>
 *
 * 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.0 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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 *
 */
/* @CPPFILE ApvlvView.cc
 *
 *  Author: Alf <naihe2010@126.com>
 */
/* @date Created: 2008/09/30 00:00:00 Alf */

#include "ApvlvParams.h"
#include "ApvlvInfo.h"
#include "ApvlvDir.h"
#include "ApvlvCmds.h"
#include "ApvlvView.h"

#ifdef APVLV_WITH_HTML
# include "ApvlvHtm.h"
#endif

#include <glib.h>
#include <glib/gprintf.h>
#include <gio/gio.h>
#include <gtk/gtk.h>
#include <gdk/gdk.h>
#include <gdk/gdkkeysyms.h>
#include <gdk-pixbuf/gdk-pixbuf.h>

#include <iostream>
#include <fstream>
#include <sstream>

namespace apvlv
{
ApvlvView *gView = NULL;

const int ApvlvView::APVLV_MENU_HEIGHT = 30;
const int ApvlvView::APVLV_CMD_BAR_HEIGHT = 20;
const int ApvlvView::APVLV_TABS_HEIGHT = 36;

ApvlvView::ApvlvView (const char *filename):mCurrTabPos (-1)
{
  mProCmd = 0;

  mCurrHistroy = -1;

  mHasFull = FALSE;

  mMainWindow = gtk_window_new (GTK_WINDOW_TOPLEVEL);

  int w = gParams->valuei ("width");
  int h = gParams->valuei ("height");

  if (gParams->valueb ("fullscreen"))
    {
      GdkScreen *scr = gdk_screen_get_default ();
      mWidth = gdk_screen_get_width (scr);
      mHeight = gdk_screen_get_height (scr);
      fullscreen ();
    }
  else
    {
      mWidth = w;
      mHeight = h;
      gtk_window_set_default_size (GTK_WINDOW (mMainWindow),
                                   w > 1 ? w : 800, h > 1 ? h : 600);
    }

  mViewBox = gtk_vbox_new (FALSE, 0);
  gtk_container_add (GTK_CONTAINER (mMainWindow), mViewBox);

  mMenu = new ApvlvMenu ();

  if (strchr (gParams->values ("guioptions"), 'm') != NULL)
    {
      mMenu->setsize (mWidth, APVLV_MENU_HEIGHT - 1);
      gtk_box_pack_start (GTK_BOX (mViewBox), mMenu->widget (), FALSE, FALSE,
                          0);
    }

  mTabContainer = gtk_notebook_new ();
  gtk_notebook_set_show_tabs (GTK_NOTEBOOK (mTabContainer), FALSE);
  gtk_notebook_set_scrollable (GTK_NOTEBOOK (mTabContainer), TRUE);
  gtk_box_pack_start (GTK_BOX (mViewBox), mTabContainer, TRUE, TRUE, 0);

  if (newtab (filename) == false)
    {
      bool ret = false;
      gchar *rpath = absolutepath (helppdf.c_str ());
      if (rpath)
        {
          ret = newtab (rpath);
          g_free (rpath);
        }

      if (ret == false)
        {
          exit (1);
        }
    }

  mCommandBar = gtk_entry_new ();
  gtk_box_pack_end (GTK_BOX (mViewBox), mCommandBar, FALSE, FALSE, 0);

  g_signal_connect (G_OBJECT (mMainWindow), "size-allocate",
                    G_CALLBACK (apvlv_view_resized_cb), this);

  g_signal_connect (G_OBJECT (mMainWindow), "key-press-event",
                    G_CALLBACK (apvlv_view_keypress_cb), this);

  g_signal_connect (G_OBJECT (mMainWindow), "delete-event",
                    G_CALLBACK (apvlv_view_delete_cb), this);
  g_signal_connect (G_OBJECT (mMainWindow), "destroy-event",
                    G_CALLBACK (apvlv_view_delete_cb), this);

  g_signal_connect (G_OBJECT (mTabContainer), "switch-page",
                    G_CALLBACK (apvlv_notebook_switch_cb), this);

  g_signal_connect (G_OBJECT (mCommandBar), "key-press-event",
                    G_CALLBACK (apvlv_view_commandbar_cb), this);

  gtk_widget_show_all (mMainWindow);

  cmd_hide ();
}

ApvlvView::~ApvlvView ()
{
  size_t i;

  delete mMenu;

  for (int i = 0; i < (int) mTabList.size (); i++)
    {
      delete mTabList[i].root;
    }
  mTabList.clear ();

  mCmdHistroy.clear ();

  for (i = 0; i < mDocs.size (); ++i)
    {
      ApvlvCore *core = (ApvlvCore *) mDocs[i];
      delete core;
    }
}

void ApvlvView::show ()
{
  gtk_main ();
}

GtkWidget *ApvlvView::widget ()
{
  return mMainWindow;
}

ApvlvWindow *ApvlvView::currentWindow ()
{
  return ApvlvWindow::currentWindow ();
}

void ApvlvView::delcurrentWindow ()
{
  ApvlvWindow::delcurrentWindow ();
  mTabList[mCurrTabPos].numwindows--;
  updatetabname ();
}

void ApvlvView::open ()
{
  gchar *dirname;

  GtkWidget *dia = gtk_file_chooser_dialog_new ("Open ...",
                   GTK_WINDOW (mMainWindow),
                   GTK_FILE_CHOOSER_ACTION_SAVE,
                   GTK_STOCK_CANCEL,
                   GTK_RESPONSE_CANCEL,
                   GTK_STOCK_OK,
                   GTK_RESPONSE_ACCEPT,
                   NULL);
  infofile *fp = gInfo->file (0);
  dirname =
    fp ? g_dirname (fp->file.c_str ()) :
    g_strdup (gParams->values ("defaultdir"));
  debug ("lastfile: [%s], dirname: [%s]", fp ? fp->file.c_str () : "",
         dirname);
  gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (dia), dirname);
  g_free (dirname);

  GtkFileFilter *filter = gtk_file_filter_new ();
  gtk_file_filter_add_mime_type (filter, "PDF File");
  gtk_file_filter_add_pattern (filter, "*.pdf");
  gtk_file_filter_add_pattern (filter, "*.PDF");
#ifdef APVLV_WITH_DJVU
  gtk_file_filter_add_mime_type (filter, "DJVU File");
  gtk_file_filter_add_pattern (filter, "*.DJV");
  gtk_file_filter_add_pattern (filter, "*.djv");
  gtk_file_filter_add_pattern (filter, "*.DJVU");
  gtk_file_filter_add_pattern (filter, "*.djvu");
#endif
#ifdef APVLV_WITH_TXT
  gtk_file_filter_add_mime_type (filter, "TXT File");
  gtk_file_filter_add_pattern (filter, "*.TXT");
  gtk_file_filter_add_pattern (filter, "*.txt");
#endif
#ifdef APVLV_WITH_UMD
  gtk_file_filter_add_mime_type (filter, "UMD File");
  gtk_file_filter_add_pattern (filter, "*.UMD");
  gtk_file_filter_add_pattern (filter, "*.umd");
#endif
#ifdef APVLV_WITH_HTML
  gtk_file_filter_add_mime_type (filter, "HTML File");
  gtk_file_filter_add_pattern (filter, "*.HTM");
  gtk_file_filter_add_pattern (filter, "*.htm");
  gtk_file_filter_add_pattern (filter, "*.HTML");
  gtk_file_filter_add_pattern (filter, "*.html");
#endif
  gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dia), filter);

  gint ret = gtk_dialog_run (GTK_DIALOG (dia));
  if (ret == GTK_RESPONSE_ACCEPT)
    {
      gchar *filename =
        gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dia));

#ifdef APVLV_WITH_HTML
      if (g_ascii_strcasecmp (filename + strlen (filename) - 4,
                              ".htm") == 0
          || g_ascii_strcasecmp (filename + strlen (filename) - 5,
                                 ".html") == 0)
        {
          loadhtml (filename);
        }
      else
#endif
        {
          loadfile (filename);
        }
      g_free (filename);
    }
  gtk_widget_destroy (dia);
}

void ApvlvView::opendir ()
{
  gchar *dirname;

  GtkWidget *dia = gtk_file_chooser_dialog_new ("",
                   GTK_WINDOW (mMainWindow),
                   GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER,
                   GTK_STOCK_CANCEL,
                   GTK_RESPONSE_CANCEL,
                   GTK_STOCK_OK,
                   GTK_RESPONSE_ACCEPT,
                   NULL);
  infofile *fp = gInfo->file (0);
  dirname = fp ? g_dirname (fp->file.c_str ()) :
            g_strdup (gParams->values ("defaultdir"));
  gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (dia), dirname);
  g_free (dirname);

  gint ret = gtk_dialog_run (GTK_DIALOG (dia));
  if (ret == GTK_RESPONSE_ACCEPT)
    {
      gchar *filename =
        gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dia));

      loaddir (filename);
      g_free (filename);
    }
  gtk_widget_destroy (dia);
}

bool ApvlvView::loaddir (const char *path)
{
  ApvlvCore *ndoc = hasloaded (path, CORE_DIR);
  if (ndoc == NULL)
    {
      int w, h;
      currentWindow ()->getsize (&w, &h);
      ndoc = new ApvlvDir (w, h);
      if (!ndoc->loadfile (path))
        {
          delete ndoc;
          return false;
        }
      regloaded (ndoc);
    }

  currentWindow ()->setCore (ndoc);
  updatetabname ();
  return true;
}

#ifdef APVLV_WITH_HTML
bool ApvlvView::loadhtml (const char *path)
{
  ApvlvCore *ndoc = hasloaded (path, CORE_DIR);
  if (ndoc == NULL)
    {
      int w, h;
      currentWindow ()->getsize (&w, &h);
      ndoc = new ApvlvHTML (w, h);
      if (!ndoc->loadfile (path))
        {
          delete ndoc;
          return false;
        }
      regloaded (ndoc);
    }

  currentWindow ()->setCore (ndoc);
  updatetabname ();
  return true;
}
#endif

bool ApvlvView::loadfile (string file)
{
  return loadfile (file.c_str ());
}

void ApvlvView::quit ()
{
  if ((int) mTabList.size () == 1)
    {
      mTabList.clear ();
      apvlv_view_delete_cb (NULL, NULL, this);
      return;
    }

  int p = mCurrTabPos;
  if (mCurrTabPos < (int) mTabList.size () - 1)
    switch_tabcontext (mCurrTabPos + 1);
  else
    switch_tabcontext (mCurrTabPos - 1);

  gtk_notebook_set_current_page (GTK_NOTEBOOK (mTabContainer), mCurrTabPos);
  mRootWindow->setsize (mWidth, adjheight ());

  gtk_notebook_remove_page (GTK_NOTEBOOK (mTabContainer), p);
  delete_tabcontext (p);

  if (mCurrTabPos > p)
    --mCurrTabPos;

  mRootWindow->setsize (mWidth, adjheight ());
}

bool ApvlvView::newtab (const char *filename)
{
  ApvlvCore *ndoc;
  ndoc = hasloaded (filename,
                    gParams->valueb ("content") ? CORE_CONTENT : CORE_DOC);

  if (ndoc == NULL)
    {
      if (gParams->valueb ("content"))
        {
          ndoc = new ApvlvDir (mWidth, adjheight ());
          if (!ndoc->loadfile (filename))
            {
              delete ndoc;
              ndoc = NULL;
            }
        }

      if (ndoc == NULL)
        {
          ndoc =
            new ApvlvDoc (mWidth, mHeight, gParams->values ("zoom"), false);
          if (!ndoc->loadfile (filename))
            {
              delete ndoc;
              ndoc = NULL;
            }
        }

      if (ndoc)
        {
          regloaded (ndoc);
        }
    }

  if (ndoc)
    {
      newtab (ndoc);
      return true;
    }
  else
    {
      return false;
    }
}

bool ApvlvView::newtab (ApvlvCore * core)
{
  int pos = new_tabcontext (core, true);

  switch_tabcontext (pos);
  mRootWindow->setsize (mWidth, adjheight ());

  gchar *base =
    core->filename ()? g_path_get_basename (core->filename ()) :
    g_strdup ("NONE");
  GtkWidget *tabname = gtk_label_new (base);
  g_free (base);

  GtkWidget *parentbox = gtk_vbox_new (false, 0);
  gtk_container_add (GTK_CONTAINER (parentbox),
                     mTabList[mCurrTabPos].root->widget ());

  gtk_notebook_insert_page (GTK_NOTEBOOK (mTabContainer), parentbox,
                            tabname, pos);

  gtk_widget_show_all (parentbox);
  gtk_notebook_set_current_page (GTK_NOTEBOOK (mTabContainer), mCurrTabPos);
  return true;
}

int ApvlvView::new_tabcontext (ApvlvCore * core, bool insertAfterCurr)
{
  ApvlvWindow *troot = new ApvlvWindow (core);
  TabEntry context (troot, troot, 1);
  if (!insertAfterCurr || mCurrTabPos == -1)
    {
      mTabList.push_back (context);
      return mTabList.size () - 1;
    }

  std::vector < TabEntry >::iterator
  insPos = mTabList.begin () + mCurrTabPos + 1;
  mTabList.insert (insPos, context);

  if (mTabList.size () > 1)
    {
      gtk_notebook_set_show_tabs (GTK_NOTEBOOK (mTabContainer), TRUE);
      gtk_widget_set_size_request (mTabContainer, mWidth - 12, 12);
    }

  return mCurrTabPos + 1;
}

void ApvlvView::delete_tabcontext (int tabPos)
{
  asst (tabPos >= 0 && tabPos < (int) mTabList.size ());

  std::vector < TabEntry >::iterator remPos = mTabList.begin () + tabPos;

  if (remPos->root != NULL)
    {
      delete remPos->root;
      remPos->root = NULL;
    }

  int c = mTabList.size ();
  mTabList.erase (remPos);
  if (c == (int) mTabList.size ())
    {
      gView->errormessage ("erase failed to remove context");
    }

  if ((int) mTabList.size () <= 1)
    {
      gtk_notebook_set_show_tabs (GTK_NOTEBOOK (mTabContainer), FALSE);
    }
}

void ApvlvView::switch_tabcontext (int tabPos)
{
  asst (tabPos >= 0 && tabPos < (int) mTabList.size ());

  if (mCurrTabPos != -1)
    mTabList[mCurrTabPos].curr = currentWindow ();

  mCurrTabPos = tabPos;
  mRootWindow = mTabList[tabPos].root;
  ApvlvWindow::setcurrentWindow (NULL, mTabList[tabPos].curr);

  if (crtadoc () && crtadoc ()->filename ())
    {
      gchar *base = g_path_get_basename (crtadoc ()->filename ());
      settitle (base);
      g_free (base);
    }
}

bool ApvlvView::loadfile (const char *filename)
{
  if (filename == NULL || *filename == '\0')
    {
      return false;
    }

  char *abpath = absolutepath (filename);
  if (abpath == NULL)
    {
      return false;
    }

  ApvlvWindow *win = ApvlvWindow::currentWindow ();
  ApvlvCore *ndoc;

  ndoc =
    hasloaded (abpath,
               gParams->valueb ("content") ? CORE_CONTENT : CORE_DOC);

  if (ndoc == NULL)
    {
      int w, h;
      win->getsize (&w, &h);
      if (gParams->valueb ("content"))
        {
          ndoc = new ApvlvDir (w, h);
          if (!ndoc->loadfile (abpath))
            {
              debug ("can't load");
              delete ndoc;
              ndoc = NULL;
            }
        }

      if (ndoc == NULL)
        {
          ndoc = new ApvlvDoc (w, h, gParams->values ("zoom"), false);
          if (!ndoc->loadfile (filename))
            {
              delete ndoc;
              ndoc = NULL;
            }
        }

      if (ndoc != NULL)
        {
          regloaded (ndoc);
        }
    }

  if (ndoc != NULL)
    {
      win->setCore (ndoc);
      updatetabname ();
    }
  g_free (abpath);

  return ndoc != NULL ? true : false;
}

ApvlvCore *ApvlvView::hasloaded (const char *abpath, int type)
{
  ApvlvCore *core;
  size_t i;
  for (i = 0; i < mDocs.size (); ++i)
    {
      core = (ApvlvCore *) mDocs[i];
      if (core->inuse () == false
          && core->type () == type
          && strcmp (core->filename (), abpath) == 0)
        {
          return core;
        }
    }
  return NULL;
}

void ApvlvView::regloaded (ApvlvCore * core)
{
  if (gParams->valuei ("pdfcache") < 2)
    {
      gParams->push ("pdfcache", "2");
    }

  if (mDocs.size () >= (size_t) gParams->valuei ("pdfcache"))
    {
      std::vector < ApvlvCore * >::iterator itr = mDocs.begin ();
      debug ("to pdf cache size: %d, remove first: %p\n",
             gParams->valuei ("pdfcache"), *itr);
      mDocs.erase (itr);
    }
  mDocs.push_back (core);
}

GCompletion *ApvlvView::filecompleteinit (const char *path)
{
  GCompletion *gcomp = g_completion_new (NULL);
  GList *list = g_list_alloc ();
  gchar *dname, *bname;
  const gchar *name;

  dname = g_path_get_dirname (path);
  GDir *dir = g_dir_open ((const char *) dname, 0, NULL);
  if (dir != NULL)
    {
      bname = g_path_get_basename (path);
      size_t len = strlen (bname);
      while ((name = g_dir_read_name (dir)) != NULL)
        {
          gchar *fname = g_locale_from_utf8 (name, -1, NULL, NULL, NULL);

          if (strcmp (bname, PATH_SEP_S) != 0)
            {
              if (strncmp (fname, bname, len) != 0)
                {
                  g_free (fname);
                  continue;
                }
            }

          if (strcmp (dname, ".") == 0)
            {
              list->data = g_strdup (fname);
            }
          else
            {
              if (dname[strlen (dname) - 1] == PATH_SEP_C)
                {
                  list->data = g_strjoin ("", dname, fname, NULL);
                }
              else
                {
                  list->data = g_strjoin (PATH_SEP_S, dname, fname, NULL);
                }
            }

          g_free (fname);

          debug ("add a item: %s", (char *) list->data);
          g_completion_add_items (gcomp, list);
        }
      g_free (bname);
      g_dir_close (dir);
    }
  g_free (dname);

  g_list_free (list);

  return gcomp;
}

void ApvlvView::promptcommand (char ch)
{
  char s[2] = { 0 };
  s[0] = ch;
  gtk_entry_set_text (GTK_ENTRY (mCommandBar), s);
  cmd_show (CMD_CMD);
}

void ApvlvView::promptcommand (const char *s)
{
  gtk_entry_set_text (GTK_ENTRY (mCommandBar), s);
  cmd_show (CMD_CMD);
}

void ApvlvView::errormessage (const char *str, ...)
{
  gchar estr[512];
  va_list vap;
  va_start (vap, str);
  vsnprintf (estr, sizeof estr, str, vap);
  va_end (vap);
  gtk_entry_set_text (GTK_ENTRY (mCommandBar), "ERROR: ");
  gtk_entry_append_text (GTK_ENTRY (mCommandBar), estr);
  cmd_show (CMD_MESSAGE);
}

void ApvlvView::infomessage (const char *str, ...)
{
  gchar estr[512];
  va_list vap;
  va_start (vap, str);
  vsnprintf (estr, sizeof estr, str, vap);
  va_end (vap);
  gtk_entry_set_text (GTK_ENTRY (mCommandBar), "INFO: ");
  gtk_entry_append_text (GTK_ENTRY (mCommandBar), estr);
  cmd_show (CMD_MESSAGE);
}

void ApvlvView::cmd_show (int cmdtype)
{
  if (mMainWindow == NULL)
    return;

  mCmdType = cmdtype;

  mRootWindow->setsize (mWidth, adjheight ());

  gtk_widget_show (mCommandBar);
  gtk_widget_grab_focus (mCommandBar);
  gtk_entry_set_position (GTK_ENTRY (mCommandBar), -1);
}

void ApvlvView::cmd_hide ()
{
  if (mMainWindow == NULL)
    return;
  mCmdType = CMD_NONE;

  gtk_widget_hide (mCommandBar);
  mRootWindow->setsize (mWidth, adjheight ());

  gtk_widget_grab_focus (mMainWindow);
}

void ApvlvView::cmd_auto (const char *ps)
{
  GCompletion *gcomp = NULL;

  stringstream ss (ps);
  string cmd, np, argu;
  ss >> cmd >> np >> argu;

  if (np[np.length () - 1] == '\\')
    {
      np.replace (np.length () - 1, 1, 1, ' ');
      np += argu;
      ss >> argu;
    }

  if (cmd == "" || np == "")
    {
      return;
    }

  if (cmd == "o" || cmd == "open" || cmd == "TOtext")
    {
      gcomp = filecompleteinit (np.c_str ());
    }
  else if (cmd == "doc")
    {
      gcomp = g_completion_new (NULL);
      GList *list = g_list_alloc ();
      size_t i;
      for (i = 0; i < mDocs.size (); ++i)
        {
          list->data = g_strdup (((ApvlvCore *) mDocs[i])->filename ());
          g_completion_add_items (gcomp, list);
        }
      g_list_free (list);
    }

  if (gcomp != NULL)
    {
      char *comtext = NULL;
      debug ("find match: %s", np.c_str ());
      g_completion_complete (gcomp, np.c_str (), &comtext);
      if (comtext != NULL)
        {
          char text[PATH_MAX];

          debug ("get a match: %s", comtext);
          gchar **v;

          v = g_strsplit (comtext, " ", -1);
          if (v != NULL)
            {
              gchar *comstr = g_strjoinv ("\\ ", v);
              g_snprintf (text, sizeof text, ":%s %s", cmd.c_str (),
                          comstr);
              g_free (comstr);
              g_strfreev (v);
            }
          else
            {
              g_snprintf (text, sizeof text, ":%s %s", cmd.c_str (),
                          comtext);
            }
          gtk_entry_set_text (GTK_ENTRY (mCommandBar), text);
          gtk_editable_set_position (GTK_EDITABLE (mCommandBar), -1);
          g_free (comtext);
        }
      else
        {
          debug ("no get match");
        }

      g_completion_free (gcomp);
    }
}

void ApvlvView::fullscreen ()
{
  if (mHasFull == false)
    {
      gtk_window_fullscreen (GTK_WINDOW (mMainWindow));
      mHasFull = true;
    }
  else
    {
      gtk_window_unfullscreen (GTK_WINDOW (mMainWindow));
      mHasFull = false;
    }
}

ApvlvCore *ApvlvView::crtadoc ()
{
  return currentWindow ()->getCore ();
}

returnType ApvlvView::subprocess (int ct, guint key)
{
  guint procmd = mProCmd;
  mProCmd = 0;
  switch (procmd)
    {
    case CTRL ('w'):
      if (key == 'q' || key == CTRL ('Q'))
        {
          if (currentWindow ()->istop ())
            {
              quit ();
            }
          else
            {
              delcurrentWindow ();
            }
        }
      else
        {
          returnType rv = currentWindow ()->process (ct, key);
          updatetabname ();
          return rv;
        }
      break;

    case 'g':
      if (ct == 0)
        ct = 1;

      if (key == 't')
        switchtab (mCurrTabPos + ct);
      else if (key == 'T')
        switchtab (mCurrTabPos - ct);
      else if (key == 'g')
        crtadoc ()->showpage (0);
      break;

    default:
      return NO_MATCH;
      break;
    }

  return MATCH;
}

returnType ApvlvView::process (int has, int ct, guint key)
{
  if (mProCmd != 0)
    {
      return subprocess (ct, key);
    }

  switch (key)
    {
    case CTRL ('w'):
      mProCmd = CTRL ('w');
      return NEED_MORE;
      break;
    case 'q':
      quit ();
      break;
    case 'f':
      fullscreen ();
      break;
    case 'g':
      mProCmd = 'g';
      return NEED_MORE;
    default:
      return crtadoc ()->process (has, ct, key);
      break;
    }

  return MATCH;
}

bool ApvlvView::run (const char *str)
{
  bool ret;

  switch (*str)
    {
    case SEARCH:
      crtadoc ()->markposition ('\'');
      ret = crtadoc ()->search (str + 1);
      break;

    case BACKSEARCH:
      crtadoc ()->markposition ('\'');
      ret = crtadoc ()->search (str + 1, true);
      break;

    case COMMANDMODE:
      ret = runcmd (str + 1);
      break;

    default:
      ret = false;
      break;
    }

  return ret;
}

void ApvlvView::settitle (const char *title)
{
  gtk_window_set_title (GTK_WINDOW (mMainWindow), title);
}

bool ApvlvView::runcmd (const char *str)
{
  bool ret = true;

  if (*str == '!')
    {
      apvlv_system (str + 1);
    }
  else
    {
      stringstream ss (str);
      string cmd, subcmd, argu;
      ss >> cmd >> subcmd >> argu;

      if (subcmd.length () > 0 && subcmd[subcmd.length () - 1] == '\\')
        {
          subcmd.replace (subcmd.length () - 1, 1, 1, ' ');
          subcmd += argu;
          ss >> argu;
        }

      if (cmd == "set")
        {
          if (subcmd == "cache")
            {
              gParams->push ("cache", "yes");
              crtadoc ()->usecache (true);
            }
          else if (subcmd == "nocache")
            {
              gParams->push ("cache", "no");
              crtadoc ()->usecache (false);
            }
          else if (subcmd == "skip")
            {
              crtadoc ()->setskip (atoi (argu.c_str ()));
            }
          else
            {
              gParams->push (subcmd, argu);
            }
        }
      else if (cmd == "map" && subcmd != "")
        {
          gCmds->buildmap (subcmd.c_str (), argu.c_str ());
        }
      else if ((cmd == "o"
                || cmd == "open" || cmd == "doc") && subcmd != "")
        {
          if (g_file_test (subcmd.c_str (), G_FILE_TEST_IS_DIR))
            {
              ret = loaddir (subcmd.c_str ());
            }
#ifdef APVLV_WITH_HTML
          else if (g_ascii_strncasecmp (subcmd.c_str (), "http://", 7) == 0
              || g_ascii_strcasecmp (subcmd.c_str () + subcmd.length () - 4,
                                     ".htm") == 0
              || g_ascii_strcasecmp (subcmd.c_str () + subcmd.length () - 5,
                                     ".html") == 0)
            {
              ret = loadhtml (subcmd.c_str ());
            }
#endif
          else if (g_file_test (subcmd.c_str (), G_FILE_TEST_EXISTS))
            {
              ret = loadfile (subcmd.c_str ());
            }
          else
            {
              errormessage ("no file '%s'", subcmd.c_str ());
              ret = false;
            }
        }
      else if (cmd == "TOtext" && subcmd != "")
        {
          crtadoc ()->totext (subcmd.c_str ());
        }
      else if ((cmd == "pr" || cmd == "print"))
        {
          crtadoc ()->print (subcmd == "" ? -1 : atoi (subcmd.c_str ()));
        }
      else if (cmd == "sp")
        {
          currentWindow ()->birth (false);
          windowadded ();
        }
      else if (cmd == "vsp")
        {
          currentWindow ()->birth (true);
          windowadded ();
        }
      else if ((cmd == "zoom" || cmd == "z") && subcmd != "")
        {
          crtadoc ()->setzoom (subcmd.c_str ());
        }
      else if (cmd == "forwardpage" || cmd == "fp")
        {
          if (subcmd == "")
            crtadoc ()->nextpage (1);
          else
            crtadoc ()->nextpage (atoi (subcmd.c_str ()));
        }
      else if (cmd == "prewardpage" || cmd == "bp")
        {
          if (subcmd == "")
            crtadoc ()->prepage (1);
          else
            crtadoc ()->prepage (atoi (subcmd.c_str ()));
        }
      else if (cmd == "goto" || cmd == "g")
        {
          crtadoc ()->markposition ('\'');
          int p = atoi (subcmd.c_str ());
          p += crtadoc ()->getskip ();
          crtadoc ()->showpage (p - 1);
        }
      else if ((cmd == "help" || cmd == "h") && subcmd == "info")
        {
          loadfile (helppdf);
          crtadoc ()->showpage (1);
        }
      else if ((cmd == "help" || cmd == "h") && subcmd == "command")
        {
          loadfile (helppdf);
          crtadoc ()->showpage (3);
        }
      else if ((cmd == "help" || cmd == "h") && subcmd == "setting")
        {
          crtadoc ()->loadfile (helppdf.c_str ());
          crtadoc ()->showpage (8);
        }
      else if ((cmd == "help" || cmd == "h") && subcmd == "prompt")
        {
          crtadoc ()->loadfile (helppdf.c_str ());
          crtadoc ()->showpage (8);
        }
      else if (cmd == "help" || cmd == "h")
        {
          loadfile (helppdf);
        }
      else if (cmd == "q" || cmd == "quit")
        {
          if (currentWindow ()->istop ())
            {
              quit ();
            }
          else
            {
              delcurrentWindow ();
            }
        }
      else if (cmd == "qall")
        {
          while (!mTabList.empty ())
            {
              if (currentWindow ()->istop ())
                {
                  quit ();
                }
              else
                {
                  delcurrentWindow ();
                }
            }
        }
      else if (cmd == "tabnew")
        {
          newtab (helppdf.c_str ());
        }
      else if (cmd == "tabn" || cmd == "tabnext")
        {
          switchtab (mCurrTabPos + 1);
        }
      else if (cmd == "tabp" || cmd == "tabprevious")
        {
          switchtab (mCurrTabPos - 1);
        }
      else if (cmd == "w" || cmd == "write")
        {
          crtadoc ()->writefile (subcmd.size () >
                                 0 ? subcmd.c_str () : NULL);
        }
      else
        {
          bool isn = true;
          for (size_t i = 0; i < cmd.length (); ++i)
            {
              if (cmd[i] < '0' || cmd[i] > '9')
                {
                  isn = false;
                  break;
                }
            }
          if (isn && crtadoc ())
            {
              int p = atoi (cmd.c_str ());
              p += crtadoc ()->getskip ();
              if (p != crtadoc ()->pagenumber ())
                {
                  crtadoc ()->showpage (p - 1);
                }
            }
          else
            {
              errormessage ("no command: '%s'", cmd.c_str ());
              ret = false;
            }
        }
    }

  return ret;
}

void
ApvlvView::apvlv_view_resized_cb (GtkWidget * wid, GtkAllocation * al,
                                  ApvlvView * view)
{
  int w, h;

  w = view->mViewBox->allocation.width - 12;
  h = view->mViewBox->allocation.height - 12;
  if (w != view->mWidth || h != view->mHeight)
    {
      view->mWidth = w;
      view->mHeight = h;
      view->mRootWindow->setsize (w, view->adjheight () - 10);
    }
}

gint
ApvlvView::apvlv_view_keypress_cb (GtkWidget * wid, GdkEvent * ev,
                                   ApvlvView * view)
{
  if (view->mCmdType == CMD_NONE)
    {
      gCmds->append ((GdkEventKey *) ev);
      return TRUE;
    }

  return FALSE;
}

gint
ApvlvView::apvlv_view_commandbar_cb (GtkWidget * wid, GdkEvent * ev,
                                     ApvlvView * view)
{
  if (view->mCmdType == CMD_CMD)
    {
      view->mInHistroy = false;

      GdkEventKey *gek = (GdkEventKey *) ev;
      if (gek->keyval == GDK_Return)
        {
          gchar *str =
            (gchar *) gtk_entry_get_text (GTK_ENTRY (view->mCommandBar));
          if (str && strlen (str) > 0)
            {
              if (view->run (str) == true)
                {
                  view->mCmdHistroy.push_back (str);
                  view->mCurrHistroy = view->mCmdHistroy.size () - 1;
                  view->cmd_hide ();
                  return TRUE;
                }
              else
                {
                  debug ("");
                  return TRUE;
                }
            }
          else
            {
              view->cmd_hide ();
              return TRUE;
            }
        }
      else if (gek->keyval == GDK_Tab)
        {
          gchar *str =
            (gchar *) gtk_entry_get_text (GTK_ENTRY (view->mCommandBar));
          if (str && strlen (str) > 0)
            {
              view->cmd_auto (str + 1);
            }
          return TRUE;
        }
      else if (gek->keyval == GDK_BackSpace)
        {
          gchar *str =
            (gchar *) gtk_entry_get_text (GTK_ENTRY (view->mCommandBar));
          if (str == NULL || strlen (str) == 1)
            {
              view->cmd_hide ();
              view->mCurrHistroy = view->mCmdHistroy.size () - 1;
              return TRUE;
            }
        }
      else if (gek->keyval == GDK_Escape)
        {
          view->cmd_hide ();
          view->mCurrHistroy = view->mCmdHistroy.size () - 1;
          return TRUE;
        }
      else if (gek->keyval == GDK_Up)
        {
          if (view->mCmdHistroy.size () == 0)
            {
              return TRUE;
            }

          view->mInHistroy = true;
          gtk_entry_set_text (GTK_ENTRY (view->mCommandBar),
                              view->mCurrHistroy > 0 ?
                              view->mCmdHistroy[view->
                                                mCurrHistroy--].c_str () :
                              view->mCmdHistroy[0].c_str ());
          return TRUE;
        }
      else if (gek->keyval == GDK_Down)
        {
          if (view->mCmdHistroy.size () == 0)
            {
              return TRUE;
            }

          view->mInHistroy = true;
          gtk_entry_set_text (GTK_ENTRY (view->mCommandBar),
                              (size_t) view->mCurrHistroy <
                              view->mCmdHistroy.size () -
                              1 ? view->mCmdHistroy[++view->
                                  mCurrHistroy].c_str () :
                              view->mCmdHistroy[view->mCmdHistroy.size () -
                                                1].c_str ());
          return TRUE;
        }


      return FALSE;
    }
  else if (view->mCmdType == CMD_MESSAGE)
    {
      debug ("");
      view->cmd_hide ();
      return TRUE;
    }

  return FALSE;
}

void
ApvlvView::apvlv_view_delete_cb (GtkWidget * wid, GtkAllocation * al,
                                 ApvlvView * view)
{
  view->mMainWindow = NULL;
  gtk_main_quit ();
}

void
ApvlvView::apvlv_notebook_switch_cb (GtkWidget * wid,
                                     GtkNotebookPage * page, guint pnum,
                                     ApvlvView * view)
{
  view->mCurrTabPos = pnum;
  view->mRootWindow = view->mTabList[pnum].root;
  ApvlvWindow::setcurrentWindow (NULL, view->mTabList[pnum].curr);
}

int ApvlvView::adjheight ()
{
  int adj = 0;
  if (gtk_notebook_get_show_tabs (GTK_NOTEBOOK (mTabContainer)))
    {
      adj += APVLV_TABS_HEIGHT;
    }
  if (mCmdType != CMD_NONE)
    {
      adj += APVLV_CMD_BAR_HEIGHT;
    }
  if (strchr (gParams->values ("guioptions"), 'm') != NULL)
    {
      adj += APVLV_MENU_HEIGHT;
    }

  return mHeight - adj;
}

void ApvlvView::switchtab (int tabPos)
{
  int ntabs = mTabList.size ();
  while (tabPos < 0)
    tabPos += ntabs;

  tabPos = tabPos % ntabs;
  switch_tabcontext (tabPos);
  mRootWindow->setsize (mWidth, adjheight ());
  gtk_notebook_set_current_page (GTK_NOTEBOOK (mTabContainer), mCurrTabPos);
}

void ApvlvView::windowadded ()
{
  mTabList[mCurrTabPos].numwindows++;
  updatetabname ();
}

void ApvlvView::updatetabname ()
{
  char tagname[26];

  const char *filename = currentWindow ()->getCore ()->filename ();
  gchar *gfilename;

  if (filename == NULL)
    gfilename = g_strdup ("None");
  else
    gfilename = g_path_get_basename (filename);

  settitle (gfilename);

  if (mTabList[mCurrTabPos].numwindows > 1)
    g_snprintf (tagname, sizeof tagname, "[%d] %s",
                mTabList[mCurrTabPos].numwindows, gfilename);
  else
    g_snprintf (tagname, sizeof tagname, "%s", gfilename);

  g_free (gfilename);

  GtkWidget *tabname = gtk_label_new (tagname);
  GtkWidget *tabwidget =
    gtk_notebook_get_nth_page (GTK_NOTEBOOK (mTabContainer), mCurrTabPos);
  gtk_notebook_set_tab_label (GTK_NOTEBOOK (mTabContainer), tabwidget,
                              tabname);
  gtk_notebook_set_current_page (GTK_NOTEBOOK (mTabContainer), mCurrTabPos);
}
}
