/* ****************************************************************************
  This file is part of KBabel

  Copyright (C) 1999-2000 by Matthias Kiefer
                            <matthias.kiefer@gmx.de>
                2001      by Stanislav Visnovsky <visnovsky@kde.org>

  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., 675 Mass Ave, Cambridge, MA 02139, USA.

**************************************************************************** */
#ifndef CATALOGMANAGERVIEW_H
#define CATALOGMANAGERVIEW_H

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <qdict.h>
#include <qlistview.h>
#include <qdatetime.h>
#include <qtimer.h>
#include <qfileinfo.h>
#include <qguardedptr.h>

#include <kdialogbase.h>
#include <kdirwatch.h>
#include <kprocess.h>
#include <qlist.h>

#include "settings.h"
#include "finddialog.h"

class CatManListItem;
class QPixmap;
class QPopupMenu;
class KBabel;
class KAction;
class CatManPreferences;
class QTextEdit;
class KProgress;
struct PoInfo;


class CatalogManagerView : public QListView
{
   Q_OBJECT
public:
   CatalogManagerView(QWidget* parent=0, const char* name=0);
   virtual ~CatalogManagerView();

   CatManSettings settings() const;
   /** clears the tree */
   virtual void clear();

   /** pauses the update timer */
   void pause(bool);

   /** if file fileWithPath exists in the treeview, this is updated */
   void updateFile(QString fileWithPath, bool force = false);

   void updateAfterSave(QString fileWithPath, PoInfo &info);

   bool isActive() const {return _active;}
   bool isStopped() const {return _stop;}

   void restoreView();
   void saveView() const;
   
public slots:
   void setSettings(CatManSettings newSettings);
   void toggleMark();
   /**
   * removes all marks in directory of current item (must be a directory)
   */
   void slotClearMarksInDir();
   void clearAllMarks();
   void toggleAllMarks();
   /**
   * traverses all childs in the directory of the current item
   * (including all subdirectories) and displays some statistics
   * about the translations. If the item is a file, its
   * parent directory is used instead.
   */
   void statistics();
   /** 
	* calls @ref Msgfmt::checkSyntax, to check the po-file of 
	* the selected item 
	*/
   void checkSyntax();
   
   QString find(FindOptions &options, QStringList &rest);

   void showLog();

   void stop(bool s = true);
   
   /**
   * Stop searching, do not try to proceed to the next file
   * @ref @find will return clear list of rest to be searched
   * and @ref QString::null, if search string was not is the last searched file
   */
   void stopSearch();
   
   /**
   * Information for this file has been read. If the file is in 
   * @ref @_readInfoFileList, it will update progress bar by emitting @ref @progress
   */
   void fileInfoRead( QString file );

signals:
   void openFile(QString filename);
   void openTemplate(QString openFilename,QString saveFileName);
   void settingsChanged(CatManSettings);
   void prepareProgressBar(QString msg,int max);
   void progress(int);
   void clearProgressBar();
   void prepareFindProgressBar(int max);
   void signalBuildTree(bool done);
   void signalSearchedFile(int count);

signals:
   void updateFinished();

protected:
   /**
   * builds the tree under dir relDir, but does not update any files
   * this functions always traverses all subdirs
   *
   * @param relDir the relative dir under the po- and pot- base directories
   * @param fast if true, no files will be updated
   *
   * @return true, if the directory contains any po or pot-files
   * @see CatalogManagerView::buildDir
   * @see CatalogManagerView::updateDir
   */
   bool buildDir(QString relDir,bool fast=true);

   /**
   * This function is traversing the real directory on the 
   * disc using baseDir as the
   * base directory and starts at baseDir+relDir
   * @param extension the extension of the files in this directory
   * @param fast if true, no file will be updated
   *
   * @return true, if the directory contains any po or pot-files
   * @see CatalogManagerView::buildDir
   * @see CatalogManagerView::updateDir
   */
   bool buildDir(const QString& baseDir,const QString& relDir, const QString extension,bool fast=true);

   /**
   * updates dir relDir and if any new subdir is added 
   * builds this with @ref buildDir
   *
   * This function doesn't enters subdirs except when a new subdir is added.
   * @see CatalogManagerView::buildDir
   */
   void updateDir(QString relDir);

   /**
   * stops the update timer and the dirwatch
   * @see KDirWatch::stop
   * @see QTimer::stop
   */
   virtual void hideEvent(QHideEvent*);
   /**
   * restarts the update timer and the dirwatch
   * @see KDirWatch::start
   * @see QTimer::start
   */
   virtual void showEvent(QShowEvent*);

   /** used for dragging */
   virtual void contentsMousePressEvent(QMouseEvent* e);
   /** used for dragging */
   virtual void contentsMouseMoveEvent(QMouseEvent* e);

protected slots:
   /** rebuilds the tree*/
   void buildTree();
   /**
   * recurse all visible files and updates them if necessary
   * @see CatManListItem::checkUpdate
   */
   void checkUpdate();

   /** this is called from KDirWatch when a directory has changed */
   void directoryChanged(const QString& dir);
   /** this is called from KDirWatch when a directory has been deleted */
   void directoryDeleted(const QString& dir);

   void showContentsMenu(QListViewItem *, const QPoint &, int col);
   /** does the default action on the currently selected item*/
   void activateItem(QListViewItem *);
   /** enables or disables some actions, depending on the status of this item */
   void checkSelected(QListViewItem *);
   /** calls @ref activateItem with the selected item as argument*/
   void slotOpenFile();
   /** emits signal @ref openTemplate */
   void slotOpenTemplate();
   /** deletes the po-file on the disc, that belongs to the selected item */
   void slotDeleteFile();
   /** toggles the mark of the selected item */
   void slotToggleMark();
   /**
   * toggles all marks in directory of current item (must be a directory)
   */
   void slotToggleMarksInDir();
   void slotDirCommand(int);
   void slotFileCommand(int);

private slots:
   void showOutput(KProcess *proc, char *buffer, int buflen);
   void processEnded(KProcess *proc);

private:
   void readMarker();
   void saveMarker();
   /** 
	* deletes item with package name (relative directory) relDir
	* and makes sure, that all subitems are removed from the lists
	*/
   void deleteDirItem(QString relDir);
   
   bool hasMatchingWords( QStringList &itemWords, QStringList &searchWords);

private:
   QDict<CatManListItem> _dirList;
   QDict<CatManListItem> _fileList;

   KDirWatch *_dirWatch;
   QTimer *_updateTimer;
   
   // list of files for which was calculated the progress bar for reading file info
   QStringList _readInfoFileList;
   // current count of already read files in @ref @_readInfoFileList.
   int _readInfoCount;
   
   CatManSettings _settings;

   QStringList _markerList;

   bool _active;
   // stopping, application quit
   bool _stop;
   // stop searching, do not proceed to the next file
   bool _stopSearch;
   int _updateNesting;

   QPtrList<KProcess> _pendingProcesses;

   QTextEdit* _logView;
   KDialogBase* _logWindow;
   QPopupMenu* _fileContentsMenu;
   QPopupMenu* _dirContentsMenu;
   QPopupMenu* _dirCommandsMenu;
   QPopupMenu* _fileCommandsMenu;
   KAction* _openAction;
   KAction* _openTemplateAction;
   KAction* _checkAction;
   KAction* _deleteAction;
   KAction* _markAction;

   /** used for starting a drag */
   QPoint _pressPos;
};



class CatManListItem : public QListViewItem
{
public:
   /** the type of this item */
   enum Type{File,Dir};

   CatManListItem(CatalogManagerView *view, QListViewItem* parent,QString fullPath
				   ,QString fullPotPath,QString package);

   /** creates the toplevel root item with package name "/" */
   CatManListItem(CatalogManagerView *view, QListView* parent,QString fullPath, QString fullPotPath);

   /** 
	* returns the package names (including relative path) of the 
	* children of this item 
	*/
   QStringList contentsList(bool onlyFiles=false) const;
   /**
   * returns the package names of all children of this item 
   * (including all subdirectries)
   * @param onlyFiles flag, if only the names of files should be returned
   * @see CatManListItem::contentsList
   */
   QStringList allChildrenList(bool onlyFiles=false) const;

   void setMarked(bool on);
   bool marked() const{ return _marked;}

   /**
   * checks if the file on the disc has changed,
   * reads information about the file and displays it
   * @param noParents flag, if the update has to include the parent
   * of the item, if the status has changed. Since at the first build of 
   * the tree, the status of every item changes, this is not useful then.
   */
   void checkUpdate(bool noParents=false);
   void forceUpdate();


   /** return the absolute filename of the po-File */
   QString poFile() const;
   /** return the absolute filename of the pot-File */
   QString potFile() const;
   /** returns the package name (inlcuding relative path to base-directory) */
   QString package(bool rootSlash=true) const;

   /** returns the package name (without path) */
   QString name() const;

   /**
   * returns the type of this item
   * @see CatManListItem::Type
   */
   Type type() const{return _type;}
   bool isDir() const;
   bool isFile() const;
   /** returns true, if the po-file exists*/
   bool hasPo() const;
   /** returns true, if the pot-file exists*/
   bool hasPot() const;
   /** 
	* @return the number of fuzzy messages in the po-file, 
	* 0 if no po-file exists
	*/
   int fuzzy() const;
   /** 
	* @return the number of untranslated messages in the po-file, 
	* @ref total if no po-file exists
	*/
   int untranslated() const;
   /** @return number of messages in the po- or pot-file */
   int total() const;
   /** 
	* @return true, if there are untranslated or fuzzy items.
	* If this item is a directory, it returns true, if a subitem
	* contains untranslated or fuzzy items
	*/
   bool needsWork() const;

   virtual QString key(int col,bool) const;
   virtual void setOpen(bool);

   /** paints the marking, if this package has no template and returns the result */
   QPixmap paintExclamation(QPixmap*);
   
   void updateAfterSave( PoInfo &po);
   
   QStringList &wordList() { return _wordList; }
   bool wordsUpdated() { return _wordListUpdated; }

private:
   void init(const QString& fullPath, const QString& fullPotPath,const QString& package);
   /**
   * updates the item
   * @param showPoInfo if true, reads information about the 
   * file using @ref Catalog::info
   * ( slow for big files )
   * @param includeChildren flag, if possible children should be updated,too
   * @param noParents flag, if parents should be updated, when state 
   * of the item has changed
   */
   void update(bool showPoInfo=true,bool includeChildren=false
				   , bool noParents=false );
   void updateParents();

private:
   /**
   * holds the date and the time this item was
   * last updated. This is used to check, if the file
   * on the disc has changed since last update.
   */
   QDateTime _lastUpdated;

   /** the po-file */
   QFileInfo _primary;
   /** the pot-file */
   QFileInfo _template;
   /**
   * The package name, includes the relative path beginning 
   * at the base directory.
   * The package name begins with "/" and if this is a directory it end with "/"
   * The root item has the package name "/"
   * @see CatManListItem::CatManListItem
   */
   QString _package;
   Type _type;
   bool _marked;

   /** flag, to detect if file has been deleted or is new */
   bool _hasPo;
   /** flag, to detect if file has been deleted or is new */
   bool _hasPot;

   /** flag, if the PO-file has a syntax error */
   bool _hasErrors;
   
   /** parent view for this item, used for stopping the activity */
   CatalogManagerView *_view;
   
   /** index of words, but it does not contain any useful information as values */
   QStringList _wordList;
   bool _wordListUpdated;
};

#endif // CATALOGMANAGERVIEW_H
