/*
 * 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 ApvlvDir.cc
 *
 *  Author: Alf <naihe2010@126.com>
 */
/* @date Created: 2009/01/03 23:28:26 Alf*/

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

#include <gtk/gtk.h>
#include <gdk/gdkkeysyms.h>
#include <glib/gstdio.h>

#include <cerrno>
#include <sys/stat.h>
#include <iostream>
#include <fstream>
#include <sstream>

namespace apvlv
{
  ApvlvDirNode::ApvlvDirNode (ApvlvDir *dir, GtkTreeIter * ir, gint p, const char *title)
  {
    mDir = dir;
    mPagenum = p;
    realname = NULL;

    GtkTreeIter nitr[1];
    gtk_tree_store_append (dir->mStore, itr, ir);
    *nitr = *itr;

    GdkPixbuf *pix = gdk_pixbuf_new_from_file_at_size (iconreg.c_str (), 40,
						       20, NULL);
    if (pix)
      {
	gtk_tree_store_set (dir->mStore, nitr, 0, this, 1, pix, 2,
			    title, -1);
	g_object_unref (pix);
      }
    else
      {
	gtk_tree_store_set (dir->mStore, nitr, 0, this, 2,
			    title, -1);
      }

  }

  ApvlvDirNode::ApvlvDirNode (ApvlvDir *dir, GtkTreeIter * ir, bool isdir,
			      const char *real, const char *file)
  {
    mDir = dir;
    mPagenum = isdir ? -1 : 0;
    g_snprintf (filename, sizeof filename, "%s", file);
    realname = g_strdup (real);

    if (isdir && gParams->valuei ("autoreload") > 0)
      {
	mGFile = g_file_new_for_path (realname);
	if (mGFile)
	  {
	    GError *error = NULL;
	    mGMonitor = g_file_monitor (mGFile, G_FILE_MONITOR_NONE, NULL, NULL);
	    if (error != NULL)
	      {
		debug ("Create file monitor failed: %s\n", error->message);
		g_error_free (error);
	      }
	  }
	else
	  {
	    mGMonitor = NULL;
	  }

	if (mGMonitor)
	  {
	    g_file_monitor_set_rate_limit (mGMonitor, gParams->valuei ("autoreload") * 1000);
	    g_signal_connect (G_OBJECT (mGMonitor), "changed", G_CALLBACK (apvlv_dirnode_monitor_callback), this);
	  }
      }

    GtkTreeIter nitr[1];
    gtk_tree_store_append (dir->mStore, nitr, ir);
    *itr = *nitr;

    GdkPixbuf *pix = gdk_pixbuf_new_from_file_at_size (isdir ? icondir.c_str () : iconpdf.c_str (), 40,
						       20, NULL);
    if (pix)
      {
	gtk_tree_store_set (dir->mStore, nitr, 0, this, 1, pix, 2,
			    file, -1);
	g_object_unref (pix);
      }
    else
      {
	gtk_tree_store_set (dir->mStore, nitr, 0, this, 2,
			    file, -1);
      }
  }

  ApvlvDirNode::~ApvlvDirNode ()
  {
    if (realname)
      {
	g_free (realname);
      }
  }

  void ApvlvDirNode::apvlv_dirnode_monitor_callback (GFileMonitor *mon, GFile *gf1, GFile *gf2, GFileMonitorEvent ev, ApvlvDirNode *node)
  {
    node->mDir->apvlv_dir_change_node (node, gf1, ev);
  }

  bool ApvlvDirNode::dest (const char **path, int *pn)
  {
    if (mPagenum == 0 && path != NULL)
      {
	*path = realname;
	return true;
      }

    else if (mPagenum > 0 && pn != NULL)
      {
	*pn = mPagenum;
	return true;
      }

    return false;
  }

  const char *ApvlvDirNode::phrase ()
  {
    return filename;
  }

  GtkTreeIter *ApvlvDirNode::iter ()
  {
    return itr;
  }

  ApvlvDir::ApvlvDir (int w, int h)
  {
    mReady = false;

    mProCmd = 0;

    mRotatevalue = 0;

    mDirNodes = NULL;

    mIndex = NULL;

    mFile = NULL;

    mStore =
      gtk_tree_store_new (3, G_TYPE_POINTER, G_TYPE_OBJECT, G_TYPE_STRING);
    mDirView = gtk_tree_view_new_with_model (GTK_TREE_MODEL (mStore));
    gtk_container_add (GTK_CONTAINER (mScrollwin), mDirView);
    gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (mDirView), FALSE);

    mSelection = gtk_tree_view_get_selection (GTK_TREE_VIEW (mDirView));
    g_signal_connect (G_OBJECT (mSelection), "changed",
		      G_CALLBACK (apvlv_dir_on_changed), this);

    /* Title Column */
    GtkCellRenderer *renderer0 = gtk_cell_renderer_pixbuf_new ();
    GtkCellRenderer *renderer = gtk_cell_renderer_text_new ();
    GtkTreeViewColumn *column = gtk_tree_view_column_new ();
    gtk_tree_view_column_pack_start (column, renderer0, FALSE);
    gtk_tree_view_column_pack_start (column, renderer, FALSE);
    gtk_tree_view_column_add_attribute (column, renderer0, "pixbuf", 1);
    gtk_tree_view_column_add_attribute (column, renderer, "text", 2);
    gtk_tree_view_column_set_sort_column_id (column, 2);
    gtk_tree_view_column_set_sort_order (column, GTK_SORT_ASCENDING);
    gtk_tree_view_append_column (GTK_TREE_VIEW (mDirView), column);
    gtk_tree_view_column_clicked (column);

    mStatus = new ApvlvDirStatus (this);

    gtk_box_pack_start (GTK_BOX (mVbox), mScrollwin, FALSE, FALSE, 0);
    gtk_box_pack_end (GTK_BOX (mVbox), mStatus->widget (), FALSE, FALSE, 0);

    gtk_widget_show_all (mVbox);

    setsize (w, h);
  }

  bool ApvlvDir::reload ()
  {
    gtk_tree_store_clear (mStore);

    if (mDirNodes)
      {
	for (GList * list = mDirNodes; list; list = g_list_next (list))
	  {
	    ApvlvDirNode *info = (ApvlvDirNode *) list->data;
	    delete info;
	  }
	g_list_free (mDirNodes);
	mDirNodes = NULL;
      }

    if (mIndex != NULL)
      {
	mFile->free_index (mIndex);
	mIndex = NULL;
      }

    if (mFile != NULL)
      {
	delete mFile;
	mFile = NULL;
      }

    loadfile (mFilestr.c_str (), FALSE);

    return true;
  }

  bool ApvlvDir::loadfile (const char *path, bool check)
  {
    gchar *rpath;

    if (path == NULL
	|| *path == '\0'
	|| (rpath = g_locale_from_utf8 (path, -1, NULL, NULL, NULL)) == NULL)
      {
	gView->errormessage ("path error: %s", path ? path : "No path");
	return false;
      }

    struct stat buf[1];
    int ret = stat (rpath, buf);
    g_free (rpath);
    if (ret < 0)
      {
	gView->errormessage ("stat error: %d:%s", errno, strerror (errno));
	return false;
      }

    if (S_ISDIR (buf->st_mode))
      {
	mType = CORE_DIR;
	mReady = walk_dir_path_index (NULL, path);
      }
    else
      {
	mType = CORE_CONTENT;
	mFile = ApvlvFile::newfile (path);

	if (mFile != NULL && (mIndex = mFile->new_index ()) != NULL)
	  {
	    for (ApvlvFileIndexIter itr = mIndex->children.begin ();
		 itr != mIndex->children.end (); ++itr)
	      {
		bool ready = walk_file_index (NULL, itr);
		if (mReady == false)
		  {
		    mReady = ready;
		  }
	      }
	  }
	else
	  {
	    mReady = false;
	  }
      }

    if (mReady)
      {
	mFilestr = path;
	mFirstSelTimer =
	  g_timeout_add (50,
			 (gboolean (*)(gpointer)) apvlv_dir_first_select_cb,
			 this);

	if (gParams->valuei ("autoreload") > 0)
	  {
	    mGFile = g_file_new_for_path (path);
	    if (mGFile)
	      {
		GError *error = NULL;
		mGMonitor = g_file_monitor (mGFile, G_FILE_MONITOR_NONE, NULL, NULL);
		if (error != NULL)
		  {
		    debug ("Create file monitor failed: %s\n", error->message);
		    g_error_free (error);
		  }
	      }
	    else
	      {
		mGMonitor = NULL;
	      }

	    if (mGMonitor)
	      {
		g_file_monitor_set_rate_limit (mGMonitor, gParams->valuei ("autoreload") * 1000);
		g_signal_connect (G_OBJECT (mGMonitor), "changed", G_CALLBACK (apvlv_dir_monitor_callback), this);
	      }
	  }
      }

    return mReady;
  }

  ApvlvDir::~ApvlvDir ()
  {
    if (mDirNodes)
      {
	for (GList * list = mDirNodes; list; list = g_list_next (list))
	  {
	    ApvlvDirNode *info = (ApvlvDirNode *) list->data;
	    delete info;
	  }
	g_list_free (mDirNodes);
      }

    if (mIndex != NULL)
      {
	mFile->free_index (mIndex);
      }

    if (mFile != NULL)
      {
	delete mFile;
      }

    delete mStatus;
  }

  returnType ApvlvDir::subprocess (int ct, guint key)
  {
    guint procmd = mProCmd;
    mProCmd = 0;
    switch (procmd)
      {
      default:
	return NO_MATCH;
	break;
      }

    return MATCH;
  }

  returnType ApvlvDir::process (int has, int ct, guint key)
  {
    if (ct == 0)
      {
	ct++;
      }

    if (mProCmd != 0)
      {
	return subprocess (ct, key);
      }

    switch (key)
      {
      case ':':
      case '/':
      case '?':
	gView->promptcommand (key);
	return NEED_MORE;

      case 'n':
	markposition ('\'');
	search ("");
	break;
      case 'N':
	markposition ('\'');
	search ("", true);
	break;

      case 't':
      case 'o':
      case GDK_KEY_Return:
	enter (key);
	break;

      case 'H':
	scrollto (0.0);
	break;
      case 'M':
	scrollto (0.5);
	break;
      case 'L':
	scrollto (1.0);
	break;

      case GDK_KEY_Up:
      case 'k':
	scrollup (ct);
	break;

      case CTRL ('n'):
      case CTRL ('j'):
      case GDK_KEY_Down:
      case 'j':
	scrolldown (ct);
      break;

      case GDK_KEY_BackSpace:
      case GDK_KEY_Left:
      case CTRL ('h'):
      case 'h':
	scrollleft (ct);
	break;

      case GDK_KEY_space:
      case GDK_KEY_Right:
      case CTRL ('l'):
      case 'l':
	scrollright (ct);
	break;

      case 'R':
	reload ();
	break;
      }

    return MATCH;
  }

  bool ApvlvDir::enter (guint key)
  {
    ApvlvDirNode *node;

    gtk_tree_model_get (GTK_TREE_MODEL (mStore), &mCurrentIter, 0, &node, -1);
    if (node == NULL)
      {
	return false;
      }

    const char *name = NULL;
    int pn = -1;
    if (!node->dest (&name, &pn))
      {
	return false;
      }

    ApvlvCore *ndoc = NULL;
    if (name != NULL)
      {
	if (gParams->valueb ("content"))
	  {
	    ndoc = new ApvlvDir (mWidth, mHeight);
	    if (!ndoc->loadfile (name))
	      {
		delete ndoc;
		ndoc = NULL;
	      }
	  }

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

	if (ndoc != NULL)
	  {
	    ((ApvlvDoc *) ndoc)->showpage (pn);
	  }
      }

    if (ndoc == NULL)
      {
	return false;
      }

    gView->regloaded (ndoc);

    switch (key)
      {
      case GDK_KEY_Return:
	ApvlvWindow::currentWindow ()->setCore (ndoc);
	break;

      case 'o':
	ApvlvWindow::currentWindow ()->birth (false, ndoc);
	break;

      case 't':
	gView->newtab (ndoc);
	break;

      default:
	return false;
      }

    return true;
  }

  void ApvlvDir::scrollup (int times)
  {
    GtkTreePath *path;

    if (!mReady
	|| (path =
            gtk_tree_model_get_path (GTK_TREE_MODEL (mStore),
                                     &mCurrentIter)) == NULL)
      {
	return;
      }

    for (gboolean ret = TRUE; times > 0 && ret; times--)
      {
	ret = gtk_tree_path_prev (path);
      }

    gtk_tree_model_get_iter (GTK_TREE_MODEL (mStore), &mCurrentIter, path);
    gtk_tree_selection_select_iter (mSelection, &mCurrentIter);
    gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (mDirView), path, NULL, TRUE,
				  0.5, 0.0);
    gtk_tree_path_free (path);

    mStatus->show ();
  }

  void ApvlvDir::scrolldown (int times)
  {
    if (!mReady)
      return;

    GtkTreeIter itr;
    gboolean ret;

    for (ret = TRUE, itr = mCurrentIter; times > 0 && ret; times--)
      {
	mCurrentIter = itr;
	ret = gtk_tree_model_iter_next (GTK_TREE_MODEL (mStore), &itr);
	if (ret)
	  {
	    mCurrentIter = itr;
	  }
      }

    gtk_tree_selection_select_iter (mSelection, &mCurrentIter);

    GtkTreePath *path =
      gtk_tree_model_get_path (GTK_TREE_MODEL (mStore), &mCurrentIter);
    if (path)
      {
	gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (mDirView), path, NULL,
				      TRUE, 0.5, 0.0);
	gtk_tree_path_free (path);
      }

    mStatus->show ();
  }

  void ApvlvDir::scrollleft (int times)
  {
    if (!mReady)
      return;

    GtkTreeIter itr;
    for (gboolean ret = TRUE; times > 0 && ret; times--)
      {
	ret =
	  gtk_tree_model_iter_parent (GTK_TREE_MODEL (mStore), &itr,
				      &mCurrentIter);
	if (ret)
	  {
	    mCurrentIter = itr;
	  }
      }

    gtk_tree_selection_select_iter (mSelection, &mCurrentIter);

    GtkTreePath *path =
      gtk_tree_model_get_path (GTK_TREE_MODEL (mStore), &mCurrentIter);
    if (path)
      {
	gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (mDirView), path, NULL,
				      TRUE, 0.5, 0.0);
	gtk_tree_view_collapse_row (GTK_TREE_VIEW (mDirView), path);
	gtk_tree_path_free (path);
      }

    mStatus->show ();
  }

  void ApvlvDir::scrollright (int times)
  {
    if (!mReady)
      return;

    GtkTreeIter itr;
    for (gboolean ret = TRUE; times > 0 && ret; times--)
      {
	ret =
	  gtk_tree_model_iter_children (GTK_TREE_MODEL (mStore), &itr,
					&mCurrentIter);
	if (ret)
	  {
	    mCurrentIter = itr;
	  }
      }

    GtkTreePath *path =
      gtk_tree_model_get_path (GTK_TREE_MODEL (mStore), &mCurrentIter);
    if (path)
      {
	gtk_tree_view_expand_to_path (GTK_TREE_VIEW (mDirView), path);
	gtk_tree_selection_select_iter (mSelection, &mCurrentIter);
	gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (mDirView), path, NULL,
				      TRUE, 0.5, 0.0);
	gtk_tree_path_free (path);
      }

    mStatus->show ();
  }

  bool ApvlvDir::search (const char *str, bool reverse)
  {
    bool next;

    if (!mReady)
      return false;

    if (*str == '\0' && mSearchStr == "")
      {
	return false;
      }

    next = true;
    if (*str != '\0')
      {
	mSearchStr = str;
	next = false;
      }

    ApvlvDirNode *info = NULL;
    gtk_tree_model_get (GTK_TREE_MODEL (mStore), &mCurrentIter, 0, &info, -1);
    if (info == NULL || mDirNodes == NULL)
      {
	gView->errormessage ("can't find word: '%s'", mSearchStr.c_str ());
	return false;
      }

    GList *list;
    for (list = mDirNodes; list; list = g_list_next (list))
      {
	if (info == list->data)
	  {
	    break;
	  }
      }

    if (list == NULL)
      {
	gView->errormessage ("can't find word: '%s'", mSearchStr.c_str ());
	return false;
      }

    if (next)
      {
	if (reverse)
	  {
	    list = g_list_previous (list);
	  }
	else
	  {
	    list = g_list_next (list);
	  }
      }

    bool wrap = gParams->valueb ("wrapscan");

    for (GList * origin = list; list != NULL;)
      {
	info = (ApvlvDirNode *) list->data;
	if (strstr (info->phrase (), mSearchStr.c_str ()) != NULL)
	  {
	    break;
	  }

	if (reverse)
	  {
	    list = g_list_previous (list);
	  }
	else
	  {
	    list = g_list_next (list);
	  }

	if (list == origin)
	  {
	    list = NULL;
	    break;
	  }

	if (list == NULL && wrap)
	  {
	    if (reverse)
	      {
		list = g_list_last (mDirNodes);
	      }
	    else
	      {
		list = mDirNodes;
	      }
	  }
      }

    if (list == NULL)
      {
	gView->errormessage ("can't find word: '%s'", mSearchStr.c_str ());
	return false;
      }

    mCurrentIter = *info->iter ();
    GtkTreePath *path =
      gtk_tree_model_get_path (GTK_TREE_MODEL (mStore), &mCurrentIter);
    if (path)
      {
	gtk_tree_view_expand_to_path (GTK_TREE_VIEW (mDirView), path);
	gtk_tree_selection_select_iter (mSelection, &mCurrentIter);
	gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (mDirView), path, NULL,
				      TRUE, 0.5, 0.0);
	gtk_tree_path_free (path);
      }

    mStatus->show ();
    return true;
  }

  void ApvlvDir::setactive (bool act)
  {
    mStatus->active (act);
    mActive = act;
  }

  bool ApvlvDir::walk_file_index (GtkTreeIter * titr, ApvlvFileIndexIter iter)
  {
    bool has = false;

    has = true;
    ApvlvDirNode *node = new ApvlvDirNode (this, titr, iter->page, iter->title.c_str ());
    mDirNodes = g_list_append (mDirNodes, node);

    for (ApvlvFileIndexIter itr = iter->children.begin ();
	 itr != iter->children.end (); ++itr)
      {
	bool chas = walk_file_index (has ? node->iter () : titr, itr);
	if (has == false)
	  {
	    has = chas;
	  }
      }

    return has;
  }

  void
  ApvlvDir::apvlv_dir_on_changed (GtkTreeSelection * selection,
				  ApvlvDir * dir)
  {
    GtkTreeModel *model;
    gtk_tree_selection_get_selected (selection, &model, &dir->mCurrentIter);
  }

  ApvlvDirStatus::ApvlvDirStatus (ApvlvDir * doc)
  {
    mDoc = doc;
    for (int i = 0; i < AD_STATUS_SIZE; ++i)
      {
	mStlab[i] = gtk_label_new ("");
	gtk_box_pack_start (GTK_BOX (mHbox), mStlab[i], FALSE, FALSE, 0);
      }
  }

  ApvlvDirStatus::~ApvlvDirStatus ()
  {
  }

  void ApvlvDirStatus::active (bool act)
  {
    for (unsigned int i = 0; i < AD_STATUS_SIZE; ++i)
      {
#if GTK_CHECK_VERSION(3, 0, 0)
        gtk_widget_override_color (mStlab[i],
                                   (act) ? GTK_STATE_FLAG_ACTIVE:
                                   GTK_STATE_FLAG_INSENSITIVE, NULL);
#else
	gtk_widget_modify_fg (mStlab[i],
			      (act) ? GTK_STATE_ACTIVE :
			      GTK_STATE_INSENSITIVE, NULL);
#endif
      }
  }

  void ApvlvDirStatus::setsize (int w, int h)
  {
    int sw[AD_STATUS_SIZE];
    sw[0] = w >> 1;
    sw[1] = sw[0] >> 1;
    sw[2] = sw[1] >> 1;
    sw[3] = sw[1] >> 1;
    for (unsigned int i = 0; i < AD_STATUS_SIZE; ++i)
      {
	gtk_widget_set_size_request (mStlab[i], sw[i], h);
      }
  }

  void ApvlvDirStatus::show ()
  {
    if (mDoc->filename ())
      {
	char temp[AD_STATUS_SIZE][256];
	gchar *bn;
	bn = g_path_get_basename (mDoc->filename ());
	g_snprintf (temp[0], sizeof temp[0], "%s", bn);
	g_snprintf (temp[1], sizeof temp[1], "apvlv");
	g_snprintf (temp[2], sizeof temp[2], "%d%%",
		    (int) (mDoc->zoomvalue () * 100));
	g_snprintf (temp[3], sizeof temp[3], "%d%%",
		    (int) (mDoc->scrollrate () * 100));
	for (unsigned int i = 0; i < AD_STATUS_SIZE; ++i)
	  {
	    gtk_label_set_text (GTK_LABEL (mStlab[i]), temp[i]);
	  }
	g_free (bn);
      }
  }

  gboolean ApvlvDir::apvlv_dir_first_select_cb (ApvlvDir * dir)
  {
    GtkTreeIter gtir;
    if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (dir->mStore), &gtir))
      {
	gtk_tree_selection_select_iter (dir->mSelection, &gtir);
      }
    return FALSE;
  }

  void ApvlvDir::apvlv_dir_change_node (ApvlvDirNode *node, GFile *gf1, GFileMonitorEvent ev)
  {
    if (ev != G_FILE_MONITOR_EVENT_DELETED
	&& ev != G_FILE_MONITOR_EVENT_CREATED)
      {
	return;
      }

    gchar * name = g_file_get_path (gf1);
    if (name == NULL)
      {
	debug ("Can't get path name.\n");
	return;
      }

    gchar *basename = g_file_get_basename (gf1);
    if (basename == NULL)
      {
	basename = g_path_get_basename (name);
      }
    if (basename == NULL)
      {
	basename = g_strdup (name);
      }

    if (g_ascii_strncasecmp (basename + strlen (basename) - 4, ".pdf", 4)
	!= 0
#ifdef APVLV_WITH_DJVU
	&& g_ascii_strncasecmp (basename + strlen (basename) - 5,
				".djvu", 5) != 0
	&& g_ascii_strncasecmp (basename + strlen (basename) - 4, ".djv",
				4) != 0
#endif
#ifdef APVLV_WITH_TXT
	&& g_ascii_strncasecmp (basename + strlen (basename) - 4,
				".txt", 4) != 0
#endif
#ifdef APVLV_WITH_UMD
	&& g_ascii_strncasecmp (basename + strlen (basename) - 4,
				".umd", 4) != 0
#endif
#ifdef APVLV_WITH_HTML
	&& g_ascii_strncasecmp (basename + strlen (basename) - 4,
				".htm", 4) != 0
	&& g_ascii_strncasecmp (basename + strlen (basename) - 4,
				".html", 5) != 0
#endif
	)
      {
	g_free (name);
	g_free (basename);
	return;
      }

    if (ev == G_FILE_MONITOR_EVENT_DELETED)
      {
	debug ("delete file: %s", name);

	GList *listnode;
	ApvlvDirNode *nnode = NULL;
	for (listnode = g_list_first (mDirNodes);
	     listnode != NULL;
	     listnode = g_list_next (listnode))
	  {
	    nnode = (ApvlvDirNode *) listnode->data;
	    if (strcmp (nnode->phrase (), basename) == 0)
	      {
		break;
	      }
	  }

	if (nnode != NULL)
	  {
	    gtk_tree_store_remove (mStore, nnode->iter ());
	    mDirNodes = g_list_remove (mDirNodes, nnode);
	    delete nnode;
	  }
      }

    else if (ev == G_FILE_MONITOR_EVENT_CREATED)
      {
	debug ("add file: %s", name);

	ApvlvDirNode *nnode = new ApvlvDirNode (this, node ? node->iter (): NULL, false, name, basename);
	mDirNodes = g_list_append (mDirNodes, nnode);
      }

    g_free (basename);
    g_free (name);
  }

  void ApvlvDir::apvlv_dir_monitor_callback (GFileMonitor *gfm, GFile *gf1, GFile *gf2, GFileMonitorEvent ev, ApvlvDir *dir)
  {
    if (dir->mType == CORE_CONTENT
	&& ev == G_FILE_MONITOR_EVENT_CHANGED)
      {
	gView->errormessage ("Contents is modified, apvlv reload it automatically");
	dir->reload ();
      }
    else if (dir->mType == CORE_DIR
	     && (ev == G_FILE_MONITOR_EVENT_DELETED
		 || ev == G_FILE_MONITOR_EVENT_CREATED)
	     )
      {
	dir->apvlv_dir_change_node (NULL, gf1, ev);
      }
  }

  bool ApvlvDir::walk_dir_path_index (GtkTreeIter * itr, const char *path)
  {
    bool has = false;
    GDir *dir = g_dir_open (path, 0, NULL);
    if (dir != NULL)
      {
	const gchar *name;
	while ((name = g_dir_read_name (dir)) != NULL)
	  {
	    if (strcmp (name, ".") == 0)
	      {
		debug ("avoid hidden file: %s", name);
		continue;
	      }

	    gchar *realname = g_strjoin (PATH_SEP_S, path, name, NULL);
	    //          debug ("add a item: %s[%s]", name, realname);

	    ApvlvDirNode *node = NULL;
	    struct stat buf[1];
	    char *wrealname =
	      g_locale_from_utf8 (realname, -1, NULL, NULL, NULL);
	    if (wrealname == NULL)
	      {
		g_free (realname);
		continue;
	      }

	    int ret = stat (wrealname, buf);
	    g_free (wrealname);

	    if (ret < 0)
	      {
		g_free (realname);
		continue;
	      }

	    if (S_ISDIR (buf->st_mode))
	      {
		node = new ApvlvDirNode (this, itr, true, realname, name);

		if (!walk_dir_path_index (node->iter (), realname))
		  {
		    gtk_tree_store_remove (mStore, node->iter ());
		    delete node;
		    node = NULL;
		  }

		if (node != NULL)
		  {
		    mDirNodes = g_list_append (mDirNodes, node);
		    has = true;
		  }
	      }
	    else if (g_ascii_strncasecmp (name + strlen (name) - 4, ".pdf", 4)
		     == 0
#ifdef APVLV_WITH_DJVU
		     || g_ascii_strncasecmp (name + strlen (name) - 5,
					     ".djvu", 5) == 0
		     || g_ascii_strncasecmp (name + strlen (name) - 4, ".djv",
					     4) == 0
#endif
#ifdef APVLV_WITH_TXT
		     || g_ascii_strncasecmp (name + strlen (name) - 4,
					     ".txt", 4) == 0
#endif
#ifdef APVLV_WITH_UMD
		     || g_ascii_strncasecmp (name + strlen (name) - 4,
					     ".umd", 4) == 0
#endif
#ifdef APVLV_WITH_HTML
		     || g_ascii_strncasecmp (name + strlen (name) - 4,
					     ".htm", 4) == 0
		     || g_ascii_strncasecmp (name + strlen (name) - 4,
					     ".html", 5) == 0
#endif
		     )
	      {
		node = new ApvlvDirNode (this, itr, false, realname, name);
		mDirNodes = g_list_append (mDirNodes, node);

		has = true;
	      }

	    g_free (realname);
	  }
      }
    g_dir_close (dir);

    return has;
  }
}

// Local Variables:
// mode: c++
// End:
