//$Id: DCFileList.cpp,v 1.24 2006/06/05 16:45:38 markus Rel $

//PROJECT     : DirComp
//SUBSYSTEM   : X-windows
//REFERENCES  :
//TODO        :
//BUGS        :
//REVISION    : $Revision: 1.24 $
//AUTHOR      : Markus Schwab
//CREATED     : 06.02.2003
//COPYRIGHT   : Copyright (C) 2003 - 2006

// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.

// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.


#include <dircomp-cfg.h>

#include <gtkmm/menu.h>
#include <gtkmm/messagedialog.h>

#include <YGP/Check.h>
#include <YGP/Trace.h>
#include <YGP/File.h>
#include <YGP/ATStamp.h>

#include <XGP/XValue.h>

#include "DCFileList.h"


//-----------------------------------------------------------------------------
// (Default-)Constructor
//-----------------------------------------------------------------------------
DCFileList::DCFileList () : files (Gtk::TreeStore::create (cols)) {
   TRACE9 ("DCFileList::DCFileList ()");
   Check3 (files);

   set_model (files);
   append_column (Glib::locale_to_utf8 (_("Original time")), cols.timeOrig);
   append_column (Glib::locale_to_utf8 (_("Comparative time")), cols.timeComp);
   append_column (Glib::locale_to_utf8 (_("Status")), cols.status);
   get_column (1)->set_alignment (0.5);
   get_column (2)->set_alignment (0.5);
   get_column (3)->set_alignment (0.5);
   get_column (0)->set_sizing (Gtk::TREE_VIEW_COLUMN_GROW_ONLY);
   get_column (1)->set_sizing (Gtk::TREE_VIEW_COLUMN_GROW_ONLY);
   get_column (2)->set_sizing (Gtk::TREE_VIEW_COLUMN_GROW_ONLY);
   get_column (3)->set_sizing (Gtk::TREE_VIEW_COLUMN_GROW_ONLY);
   get_column (0)->set_min_width (245);
   get_column (1)->set_min_width (120);
   get_column (2)->set_min_width (120);
   get_column (3)->set_min_width (50);
   get_column (0)->set_resizable ();
   get_column (1)->set_resizable ();
   get_column (2)->set_resizable ();
   get_column (3)->set_resizable ();
}

//-----------------------------------------------------------------------------
/// Destructor
//-----------------------------------------------------------------------------
DCFileList::~DCFileList () {
}


//-----------------------------------------------------------------------------
/// Retrieves the file name of the passed line; which is considered to be
/// stored within the data of the listbox
/// \param line: Line in list to get the filename from
/// \returns std::string: Filename
//-----------------------------------------------------------------------------
std::string DCFileList::getFilename (const Gtk::TreeIter& line) const {
   TRACE9 ("DCFileList::getFilename (const Gtk::TreeIter&)");
   Check3 (files);

   const Gtk::TreeRow row (*line);
   YGP::File* pFile (row[cols.fileOrig] ? row[cols.fileOrig] : row[cols.fileComp]);
   Check3 (pFile);
   return std::string (pFile->path ()) + std::string (pFile->name ());
}

//-----------------------------------------------------------------------------
/// Sets the file which is displayed in the specified line
/// \param line: Line in list to get the filename from
/// \param file: File to display in the line
//-----------------------------------------------------------------------------
void DCFileList::setFilename (Gtk::TreeIter& line, const std::string& file) {
   TRACE9 ("DCFileList::setFilename (Gtk::TreeIter, const std::string&) - " << file);
   Check3 (files);

   Gtk::TreeRow row (*line);
   YGP::File* objFile (new YGP::File (file.c_str ()));

   row[cols.fileOrig] ? delete row[cols.fileOrig] : delete row[cols.fileComp];
   row[cols.fileOrig] ? row[cols.fileOrig] : row[cols.fileComp] = objFile;

   XFileList::setFilename (line, file);
}

//-----------------------------------------------------------------------------
/// Adds further menus to the default popup-menu
/// \param menu: Menu where to add some entries to
/// \param line: Line for which to add entries
//-----------------------------------------------------------------------------
void DCFileList::addMenus (Gtk::Menu& menu, const Gtk::TreeIter& line) {
   TRACE5 ("DCFileList::addMenus (menu, unsigned int) - " << files->get_path (line).to_string ());
   Check3 (files);
   const Gtk::TreeRow row (*line);

   if (row[cols.fileOrig] && row[cols.fileComp]) {
      menu.items ().push_back (Gtk::Menu_Helpers::SeparatorElem ());

#if HAVE_DIFF == 1
      menu.items ().push_back
         (Gtk::Menu_Helpers::MenuElem
          (Glib::locale_to_utf8 (_("Compare with diff ...")),
           bind (mem_fun (*this, &DCFileList::startDiff), line)));
#endif
#if HAVE_XXDIFF == 1
      menu.items ().push_back
         (Gtk::Menu_Helpers::MenuElem
          (Glib::locale_to_utf8 (_("Compare with xxdiff ...")),
           bind (mem_fun (*this, &DCFileList::execute), "xxdiff", line)));
#endif
#if HAVE_KOMPARE == 1
      menu.items ().push_back
         (Gtk::Menu_Helpers::MenuElem
          (Glib::locale_to_utf8 (_("Compare with Kompare ...")),
           bind (mem_fun (*this, &DCFileList::execute), "kompare", line)));
#endif
#if (HAVE_DIFF == yes) || (HAVE_XXDIFF == yes) || (HAVE_KOMPARE == yes)
      menu.items ().push_back (Gtk::Menu_Helpers::SeparatorElem ());
#endif

      menu.items ().push_back
         (Gtk::Menu_Helpers::MenuElem
          (Glib::locale_to_utf8 (_("Copy original to comparison")),
           bind (mem_fun (*this, &DCFileList::execute), "cp", line)));
      menu.items ().push_back
         (Gtk::Menu_Helpers::MenuElem
          (Glib::locale_to_utf8 (_("Copy comparison to original")),
           bind (mem_fun (*this, &DCFileList::execute2), "cp", line)));
   }
}

//-----------------------------------------------------------------------------
/// Starts the diff-program in its own terminal
/// \param posObject: Position of files in the listbox
//-----------------------------------------------------------------------------
void DCFileList::startDiff (const Gtk::TreeIter& line) {
   const char* term (getenv ("TERM"));
   const char* pager (getenv ("PAGER"));
   if (term && pager) {
      Check3 (files);
      const Gtk::TreeRow row (*line);
      Check3 (row[cols.fileOrig] && row[cols.fileComp]);

      YGP::File* pFile (row[cols.fileOrig]);
      std::string orig (std::string (pFile->path ())
                        + std::string (pFile->name ()));
      pFile = (row[cols.fileComp]);
      std::string comp (std::string (pFile->path ())
                        + std::string (pFile->name ()));
      std::string cmd ("diff ");
      cmd += orig;
      cmd += ' ';
      cmd += comp;
      cmd += "|$PAGER ";

      TRACE9 ("DCFileList::startDiff (unsigned int) - Cmd: " << cmd);
      const char* args[] = { term, "-T", cmd.c_str (), "-e", "sh", "-c",
                             cmd.c_str (), NULL };
      execProgram (term, args, false);
   }
   else {
      Glib::ustring err (Glib::locale_to_utf8 (_("Environment variable `%1' not defined!")));
      err.replace (err.find ("%1"), 2, (term ? "PAGER" : "TERM"));
      Gtk::MessageDialog dlg (err, false, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_OK);
      dlg.run ();
   }
}

//-----------------------------------------------------------------------------
/// Starts the passed program, passing original and comparision
/// \param file: File to execute
/// \param posObject: Position of files in the listbox
//-----------------------------------------------------------------------------
void DCFileList::execute (const char* file, const Gtk::TreeIter& line) {
   Check3 (files);
   const Gtk::TreeRow row (*line);
   Check3 (row[cols.fileOrig] && row[cols.fileComp]);

   YGP::File* pFile (row[cols.fileOrig]);
   std::string orig (std::string (pFile->path ())
                     + std::string (pFile->name ()));
   pFile = (row[cols.fileComp]);
   std::string comp (std::string (pFile->path ())
                     + std::string (pFile->name ()));
   const char* args[] = { file, orig.c_str (), comp.c_str (), NULL };
   execProgram (args[0], args, false);
}

//-----------------------------------------------------------------------------
/// Starts the passed program, passing comparision and original
/// \param file: File to execute
/// \param posObject: Position of files in the listbox
//-----------------------------------------------------------------------------
void DCFileList::execute2 (const char* file, const Gtk::TreeIter& line) {
   Check3 (files);
   const Gtk::TreeRow row (*line);
   Check3 (row[cols.fileOrig] && row[cols.fileComp]);

   YGP::File* pFile (row[cols.fileOrig]);
   std::string orig (std::string (pFile->path ())
                     + std::string (pFile->name ()));
   pFile = (row[cols.fileComp]);
   std::string comp (std::string (pFile->path ())
                     + std::string (pFile->name ()));
   const char* args[] = { file, comp.c_str (), orig.c_str (), NULL };
   execProgram (args[0], args, false);
}

//-----------------------------------------------------------------------------
/// Appends a directory as last parent-entry of the listbox
/// \param dir: Name of the directory to add
//-----------------------------------------------------------------------------
void DCFileList::appendDirectory (const std::string& dir) {
   TRACE9 ("DCFileList::appendDirectory (const std::string&) - " << dir);
   Check1 (dir.size ());

   if (directories.find (dir) == directories.end ()) {
      Gtk::TreeIter iter (files->append ());
      directories[dir] = iter;

      Gtk::TreeModel::Row row (*iter);
      row[cols.name] = dir;
      row[cols.fileOrig] = NULL;
      row[cols.fileComp] = NULL;
   }
}

//-----------------------------------------------------------------------------
/// Appends a file to the last parent-entry of the listbox
/// \param prefix: String to prepend filenames with
/// \param orig: Pointer to original file (or NULL)
/// \param comp: Pointer to compared file (or NULL)
/// \param stat: String describing the difference
//-----------------------------------------------------------------------------
void DCFileList::appendFile (const std::string& prefix, const YGP::File* orig,
			     const YGP::File* comp, const std::string& stat) {
   Check1 (orig || comp); Check1 (!stat.empty ());
   const YGP::File* pFile (orig ? orig : comp); Check3 (pFile);
   const char* pName (pFile->name ());

   TRACE3 ("DCFileList::appendFile (const string&, const YGP::File* (2x), const string&) - " << prefix << ": " << pName);

   // Add file(s) to the fitting node or as root-entry, if prefix is empty
   Check3 (files);
   Gtk::TreeIter iter;
   if (prefix.size ()) {
      Check1 (directories.find (prefix) != directories.end ());
      iter = (files->append (directories[prefix]->children ()));
   }
   else
      iter = (files->append ());

   Gtk::TreeModel::Row row (*iter);
   row[cols.icon] = XGP::XFileList::getIcon4File (*pFile);
   row[cols.name] = Glib::filename_to_utf8 (pName);

   // Store node, if its a directory
   if (pFile->isDirectory ()) {
      std::string dir (prefix);
      dir += pFile->name ();
      dir += YGP::File::DIRSEPARATOR;
      TRACE9 ("DCFileList::appendFile (const string&, const YGP::File* (2x), const string&) - Add dir " << dir)
      Check2 (directories.find (dir) == directories.end ());
      directories[dir] = iter;
   }

   if (orig) {
       YGP::ATimestamp t (orig->time (), false);
       row[cols.timeOrig] = t.toString ();
   }

   if (comp) {
       YGP::ATimestamp t (comp->time (), false);
       row[cols.timeComp] = t.toString ();
   }
   row[cols.status] = stat;
   row[cols.fileOrig] = orig ? new YGP::File (*orig) : NULL;
   row[cols.fileComp] = comp ? new YGP::File (*comp) : NULL;
}

//-----------------------------------------------------------------------------
/// Callback while deleting a row
/// \param row: Row to delete
//-----------------------------------------------------------------------------
void DCFileList::deleteRow (const Gtk::TreeIter& row) {
   TRACE1 ("DCFileList::deleteRow (const Gtk::TreeModel::Iter&) - " << getFilename (row));

   while (row->children ().size ())
      deleteRow (row->children ().begin ());

   Gtk::TreeRow irow (*row);
   delete irow[cols.fileOrig];
   delete irow[cols.fileComp];
   irow[cols.fileOrig] = NULL;
   irow[cols.fileComp] = NULL;
   files->erase (row);
}

//-----------------------------------------------------------------------------
/// Deletes all lines from the model, while caring about freeing the files
//-----------------------------------------------------------------------------
void DCFileList::clear () {
   while (files->children ().size ())
      deleteRow (files->children ().begin ());
}


syntax highlighted by Code2HTML, v. 0.9.1