/***************************************************************************
 *   Copyright (C) 2004, 2005 Thomas Nagy                                  *
 *   tnagy2^8@yahoo.fr                                                     *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License version 2        *
 *   as published by the Free Software Foundation (see COPYING)            *
 *                                                                         *
 *   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.                          *
 ***************************************************************************/

#include <qpaintdevicemetrics.h>
#include <qpainter.h>
#include <qfile.h>
#include <qtextstream.h>
#include <qmultilineedit.h>
#include <qwmatrix.h>
#include <qiconset.h>
#include <qpixmapcache.h>
#include <qregexp.h>

#include <kprinter.h>
#include <kiconloader.h>
#include <kiconeffect.h>
#include <kaboutdata.h>
#include <klocale.h>
#include <kinstance.h>
#include <kaction.h>
#include <kstdaction.h>
#include <kfiledialog.h>
#include <kio/netaccess.h>
#include <kio/job.h>
#include <kurl.h>
#include <krun.h>
#include <kmessagebox.h>
#include <kstandarddirs.h>
#include <kdebug.h>
#include <klibloader.h>
#include <ktempfile.h>
#include <kparts/genericfactory.h>
#include <kparts/event.h>
#include <kmacroexpander.h>

#include <fstream>

#include "generatorwizard.h"

#include "aux.h"
#include "DDataItem.h"
#include "docsettingsdlg.h"
#include "DGuiView.h"
#include "DDataControl.h"
#include "DCanvasView.h"
#include "DTreeListView.h"
#include "DGenerator.h"
#include "TreeListViewFrame.h"

#include "KDissertPart.h"

typedef KParts::GenericFactory<KDissertPart> KDissertPartFactory;
K_EXPORT_COMPONENT_FACTORY( libkdissertpart, KDissertPartFactory );

KDissertPart::KDissertPart( QWidget *parentWidget, const char *widgetName, 
		QObject *parent, const char *name, const QStringList & /*args*/ )
: KParts::ReadWritePart(parent, name)
{
	m_caption = QString::null;
	m_lastid = DItem::NOITEM;

	// we need an instance
	setInstance( KDissertPartFactory::instance() );

	// add the translations
	KGlobal::locale()->insertCatalogue( "kdissert" );

	// create the data and the views
	m_data = new DDataControl();

	m_canvasview = new DCanvasView( parentWidget, widgetName );
	m_canvasview->setDataTree(m_data);
	m_canvasview->setFrameStyle(QFrame::NoFrame);

	m_treelistframe = new TreeListViewFrame( m_canvasview, "tree view widget" );
	m_treelistview = m_treelistframe->tree();
	m_treelistview->setDataTree(m_data);

	m_canvasview->setFocusPolicy( QWidget::ClickFocus );
	setWidget( m_canvasview );

	// set our XML-UI resource file
	setXMLFile("kdissertpart.rc");

	// do this *after* calling setWidget
	KActionCollection *ac = actionCollection();
	//ac->setWidget( m_canvasview );

	new KAction(i18n("&Clear"), 0,  this, SLOT(clearDocument()), 
			ac, "kdissert_clear");
	new KAction(i18n("&Document Properties"), 0,  this, SLOT(docProperties()),
			ac, "kdissert_docproperties");
	new KAction(i18n("&Generate Documents..."), 0, this, SLOT(generateDocument()), 
			ac, "kdissert_generate");
	m_regenerate = new KAction(i18n("&Regenerate Document"), 0, this, SLOT(regenerateDocument()),
			ac, "kdissert_reexport");
	new KAction(i18n("&Create Picture..."), 0, this, SLOT(savePic()),
			ac, "kdissert_savepic");

	new KAction(i18n("Import Data..."), 0, this, SLOT(importData()),
			ac, "kdissert_import");
	
	m_select = new KRadioAction(i18n("&Select Leaves"), "kdissert_point",
			"Ctrl+j", this, SLOT(setPointMode()), ac, "kdissert_point");
	m_link = new KRadioAction(i18n("&Link Leaves"), "kdissert_link",
			"Ctrl+k", this, SLOT(setLinkMode()), ac, "kdissert_link");
	m_sort = new KRadioAction(i18n("S&ort Subtrees"), "kdissert_sort",
			"Ctrl+l", this, SLOT(setSortMode()), ac, "kdissert_sort");
	m_scroll = new KRadioAction(i18n("S&croll"), SmallIcon("move"),
			"Ctrl+m", this, SLOT(setScrollMode()), ac, "kdissert_scroll");

	m_select->setExclusiveGroup("Hades");
	m_link->setExclusiveGroup("Hades");
	m_sort->setExclusiveGroup("Hades");
	m_scroll->setExclusiveGroup("Hades");

	m_select->setChecked(true);

	KActionMenu *zoommenu = new KActionMenu(i18n("&Zoom"), ac, "kdissert_zoom");
	m_zoom20  = new KRadioAction(i18n(" 20%"), 0, 0, this, SLOT(focus20()), ac, "kdissert_focus20");
	m_zoom35  = new KRadioAction(i18n(" 35%"), 0, 0, this, SLOT(focus35()), ac, "kdissert_focus35");
	m_zoom50  = new KRadioAction(i18n(" 50%"), 0, 0, this, SLOT(focus50()), ac, "kdissert_focus50");
	m_zoom75  = new KRadioAction(i18n(" 75%"), 0, 0, this, SLOT(focus75()), ac, "kdissert_focus75");
	m_zoom100 = new KRadioAction(i18n("100%"), 0, 0, this, SLOT(focus100()), ac, "kdissert_focus100");
	m_zoom125 = new KRadioAction(i18n("125%"), 0, 0, this, SLOT(focus125()), ac, "kdissert_focus125");
	m_zoom150 = new KRadioAction(i18n("150%"), 0, 0, this, SLOT(focus150()), ac, "kdissert_focus150");
	m_zoom200 = new KRadioAction(i18n("200%"), 0, 0, this, SLOT(focus200()), ac, "kdissert_focus200");

	zoommenu->insert(m_zoom20);
	zoommenu->insert(m_zoom35);
	zoommenu->insert(m_zoom50);
	zoommenu->insert(m_zoom75);
	zoommenu->insert(m_zoom100);
	zoommenu->insert(m_zoom125);
	zoommenu->insert(m_zoom150);
	zoommenu->insert(m_zoom200);

	m_zoom20->setExclusiveGroup("Thanatos");
	m_zoom35->setExclusiveGroup("Thanatos");
	m_zoom50->setExclusiveGroup("Thanatos");
	m_zoom75->setExclusiveGroup("Thanatos");
	m_zoom100->setExclusiveGroup("Thanatos");
	m_zoom125->setExclusiveGroup("Thanatos");
	m_zoom150->setExclusiveGroup("Thanatos");
	m_zoom200->setExclusiveGroup("Thanatos");
	
	KStdAction::zoomIn(this, SLOT(focusIn()), ac, "view_zoom_in");
	KStdAction::zoomOut(this, SLOT(focusOut()), ac, "view_zoom_out");
	
	KActionMenu *focusmenu = new KActionMenu(i18n("&Focus"), ac, "kdissert_focus");
	focusmenu->insert( new KAction(i18n("&Focus on Root"), "Ctrl+h", m_canvasview, 
				SLOT(focusOnRoot()), ac, "kdissert_focusroot"));
	focusmenu->insert( new KAction(i18n("Focus on Next Root"), "Ctrl+,", m_canvasview, 
				SLOT(focusOnNextRoot()), ac, "kdissert_nextroot"));
	focusmenu->insert( new KAction(i18n("Go Item Up"), "Ctrl+Up", m_canvasview, 
				SLOT(selectObjUp()), ac, "kdissert_goup"));
	focusmenu->insert( new KAction(i18n("Go Item Down"), "Ctrl+Down", m_canvasview, 
				SLOT(selectObjDown()), ac, "kdissert_godown"));
	focusmenu->insert( new KAction(i18n("Cycle Item Next"), "Ctrl+Left", m_canvasview,
				SLOT(selectObjLeft()), ac, "kdissert_goleft"));
	focusmenu->insert( new KAction(i18n("Cycle Item Previous"), "Ctrl+Right", m_canvasview, 
				SLOT(selectObjRight()), ac, "kdissert_goright"));
	
	KActionMenu *movemenu = new KActionMenu(i18n("&Move Selection"), ac, "kdissert_move");
	movemenu->insert( new KAction(i18n("Move Objects Up"), "Alt+Up", m_canvasview, 
				SLOT(moveSelObjectsUp()), ac, "kdissert_mvup"));
	movemenu->insert( new KAction(i18n("Move Objects Down"), "Alt+Down", m_canvasview,
				SLOT(moveSelObjectsDown()), ac, "kdissert_mvdown"));
	movemenu->insert( new KAction(i18n("Move Objects Left"), "Alt+Left", m_canvasview,
				SLOT(moveSelObjectsLeft()), ac, "kdissert_mvleft"));
	movemenu->insert( new KAction(i18n("Move Objects Right"), "Alt+Right", m_canvasview,
				SLOT(moveSelObjectsRight()), ac, "kdissert_mvright"));

	KActionMenu *addmenu = new KActionMenu(i18n("&Add Objects"), ac, "kdissert_add");
	addmenu->insert( new KAction(i18n("Add Mindmap Child"), "Insert", m_canvasview, 
				SLOT(addChild()), ac, "kdissert_childadd"));
	addmenu->insert( new KAction(i18n("Add Mindmap Sibling"), "Return", m_canvasview,
			SLOT(addSibling()), ac, "kdissert_siblingadd"));


	new KAction(i18n("&Reorganize Map"), "Ctrl+g", m_canvasview, SLOT(tidyClean()), ac, "kdissert_tidy");

	m_saveAction = KStdAction::save(this, SLOT(slotFileSave()), ac);
	KStdAction::saveAs(this, SLOT(slotFileSaveAs()), ac);
	KStdAction::print(this, SLOT(slotPrintMap()), ac);
	KStdAction::spelling(m_data, SLOT(slotFileSpell()), ac);

	m_undo = KStdAction::undo(m_data, SLOT(slotUndo()), ac, "edit_undo");
	m_redo = KStdAction::redo(m_data, SLOT(slotRedo()), ac, "edit_redo");
	connect(m_data, SIGNAL(undoState(bool)), this, SLOT(setUndoState(bool)) );
	connect(m_data, SIGNAL(redoState(bool)), this, SLOT(setRedoState(bool)) );
	
	m_warning  = new KToggleAction(i18n("Warning"), "messagebox_warning", 0,
			this, SLOT(checkFlags()), ac, "flag_warning");
	m_good     = new KToggleAction(i18n("Good"), "bookmark", 0,
			this, SLOT(checkFlags()), ac, "flag_good");
	m_idea     = new KToggleAction(i18n("Idea"), "ktip", 0,
			this, SLOT(checkFlags()), ac, "flag_idea");
	m_work     = new KToggleAction(i18n("Work Needed"), "package_settings", 0,
			this, SLOT(checkFlags()), ac, "flag_work");
	m_clarify  = new KToggleAction(i18n("Clarify"), "xmag", 0,
			this, SLOT(checkFlags()), ac, "flag_clarify");
	m_question = new KToggleAction(i18n("Question"), "help", 0,
			this, SLOT(checkFlags()), ac, "flag_question");
	m_trash    = new KToggleAction(i18n("Trash"), "trashcan_empty", 0,
			this, SLOT(checkFlags()), ac, "flag_trash");
	m_meeting  = new KToggleAction(i18n("Meeting"), "penguin", 0,
			this, SLOT(checkFlags()), ac, "flag_meeting");
	m_trouble  = new KToggleAction(i18n("Trouble There"), "ksplash", 0,
			this, SLOT(checkFlags()), ac, "flag_trouble");

	m_default= new KRadioAction(i18n("Default Color"), "konqueror", 0, this, SLOT(applyColors()), ac, "defcolor");
	m_theme1 = new KRadioAction(i18n("Theme 1"), "konqueror", 0, this, SLOT(applyColors()), ac, "theme1");
	m_theme2 = new KRadioAction(i18n("Theme 2"), "konqueror", 0, this, SLOT(applyColors()), ac, "theme2");
	m_theme3 = new KRadioAction(i18n("Theme 3"), "konqueror", 0, this, SLOT(applyColors()), ac, "theme3");
	m_theme4 = new KRadioAction(i18n("Theme 4"), "konqueror", 0, this, SLOT(applyColors()), ac, "theme4");
	m_theme5 = new KRadioAction(i18n("Theme 5"), "konqueror", 0, this, SLOT(applyColors()), ac, "theme5");
	m_theme6 = new KRadioAction(i18n("Theme 6"), "konqueror", 0, this, SLOT(applyColors()), ac, "theme6");
	m_custom = new KRadioAction(i18n("Custom Color"), "colors", 0, this, SLOT(applyColors()), ac, "customcol");
	
	m_default->setExclusiveGroup("Anubis");
	m_theme1->setExclusiveGroup("Anubis");
	m_theme2->setExclusiveGroup("Anubis");
	m_theme3->setExclusiveGroup("Anubis");
	m_theme4->setExclusiveGroup("Anubis");
	m_theme5->setExclusiveGroup("Anubis");
	m_theme6->setExclusiveGroup("Anubis");
	m_custom->setExclusiveGroup("Anubis");
	
	// enable the connections
	connect( m_canvasview, SIGNAL( actionType(int) ), this, SLOT(setActionType(int)) );
	connect( m_data, SIGNAL(documentChanged(bool)), this, SLOT(setModified(bool)) );
	connect( m_data, SIGNAL(itemChanged(int)), this, SLOT(setItemChanged(int)) );
	connect( m_data, SIGNAL(itemSelected(int, DGuiView*)), this, SLOT(itemChanged(int, DGuiView*)) );
	connect( m_data, SIGNAL(tip(const QString &)), this, SLOT(emitStatusBarMessage(const QString &)) );

	m_zoom100->activate();
	
	// we are read-write by default
	setReadWrite(true);

	// we are not modified since we have not done anything yet
	setModified(false);

	// load the global kdissert settings
	settingsChanged();

	m_default->setEnabled(false);
	m_theme1->setEnabled(false);
	m_theme2->setEnabled(false);
	m_theme3->setEnabled(false);
	m_theme4->setEnabled(false);
	m_theme5->setEnabled(false);
	m_theme6->setEnabled(false);
	m_custom->setEnabled(false);

	m_undo->setEnabled(false);
	m_redo->setEnabled(false);

	m_regenerate->setEnabled(false);

	connect( m_canvasview, SIGNAL(selectionChanged()), this, SLOT(updateColorActionsState()) );
}

KDissertPart::~KDissertPart()
{
	actionCollection()->clear();
	// The canvas is already deleted since the views call delete part->widget()
	// and widget calls delete part throught a slot call
	// delete m_canvasview;
	delete m_treelistframe;
	delete m_data;
}

KAboutData *KDissertPart::createAboutData()
{
	// The non-i18n name here must be the same as the directory in
	// which the part's rc file is installed
	KAboutData *aboutData = new KAboutData("kdissertpart", I18N_NOOP("KDissertPart"), "1.0");
	aboutData->addAuthor("Thomas Nagy", 0, "tnagy2^8@yahoo.fr");
	return aboutData;
}

void KDissertPart::slotFileSave()
{
	saveFile();
}

void KDissertPart::slotFileSaveAs()
{
	saveFileAs();
}

void KDissertPart::setReadWrite(bool rw)
{
	// notify the views of the read-write state
	m_canvasview->setReadWrite(rw);
	m_treelistview->setReadWrite(rw);

	ReadWritePart::setReadWrite(rw);
}

void KDissertPart::setModified(bool modified)
{
	// we either enable or disable it based on the current state
	if (modified) m_saveAction->setEnabled(true);
	else m_saveAction->setEnabled(false);

	// in any event, we want our parent to do it's thing
	ReadWritePart::setModified(modified);
	checkReExportState();

	emit stateChanged();
}

void KDissertPart::checkReExportState()
{
	// check the state of the re-export action
	bool isok = m_data->canGenerate(); // can we really generate a document?
	isok &= (m_data->m_generator_lasturl.length() > 0); // is the project location still valid?
	isok &= m_data->m_generator_lastgen.size()>0; // are there any targets?
	m_regenerate->setEnabled(isok);
}

void KDissertPart::slotFileOpen()
{
	KURL url = KFileDialog::getOpenURL(QString::null,
			i18n("*.kdi|kdissert Project (*.kdi)") +"\n"+
			i18n("*.mm|Freemind Files (*.mm)") +"\n"+
			i18n("*.kno|Knowit Files (*.kno)") ,
			m_canvasview, i18n("Open Mindmap Project"));
	if (url.isEmpty())
		return;

	openURL(url);
}

bool KDissertPart::openURL(KURL url)
{
	if (! url.isValid()) return false;

	bool result = m_data->loadFromFile(url);
	if (result)
	{
		m_canvasview->focusOnRoot();
		setModified( false );
		m_url = url;
		m_data->setProject(m_url);
		emit accessed(url);

		setWindowCaption( url.prettyURL() );
	}
	return result;
}

bool KDissertPart::openFile()
{
	// TODO : never tried as a standalone kpart
	//    kdWarning()<<"KDissertPart::openFile"<<endl;

	// standard filedialog
	KURL url = KFileDialog::getOpenURL(QString::null,
			i18n("*.kdi|kdissert Project (*.kdi)") +"\n"+
			i18n("*.mm|Freemind Files (*.mm)") +"\n"+
			i18n("*.kno|Knowit Files (*.kno)"),
			m_canvasview, i18n("Open Mindmap Project"));
	if (url.isEmpty())
		return false;

	return openURL(url);
}

bool KDissertPart::saveFileAs()
{
	// this slot is called whenever the File->Save As menu is selected,
	m_url = KFileDialog::getSaveURL(QString::null, i18n("*.kdi|kdissert Project (*.kdi)"), 
			m_canvasview, i18n("Save kdissert Project"));
	if (!m_url.isEmpty() && m_url.isValid())
		return saveFile();

	return false;
}

bool KDissertPart::saveFile()
{
	// if we aren't read-write, return immediately
	if (isReadWrite() == false)
		return false;

	if ( m_url.isEmpty() || !m_url.isValid() )
	{
		return saveFileAs();
	}
	else
	{
		QString fname = m_url.url();
        	if (! fname.endsWith(".kdi", false)) fname.append(".kdi");
		KURL l_oTmpURL(fname);
		m_url=l_oTmpURL;

		bool savestatus = m_data->saveToFile(l_oTmpURL);
		if ( savestatus )
		{
			m_data->setProject(l_oTmpURL);
			emit accessed(l_oTmpURL);
			emit setStatusBarText(i18n("The project was saved")+" "+l_oTmpURL.prettyURL());
			emit stateChanged();
			return savestatus;
		}
		else
		{
			return saveFileAs();
		}
	}
	return true;
}

void KDissertPart::clearDocument()
{
	if (!m_data)
		return;

	int result = KMessageBox::warningContinueCancel(
			m_canvasview,
			i18n("You are about to clear the entire document. Are you sure?"),
			i18n("Clear Entire Document"),
			i18n("Clear"));

	if (result == KMessageBox::Continue)
		m_data->clearDocument();
}

void KDissertPart::regenerateDocument()
{
	if (!m_data) return;
	
	if (!m_data->canGenerate())
	{
		kdWarning()<<"BUG : KDissertPart::regenerateDocument (action should be disabled)"<<endl;
		m_data->m_generator_lastgen.clear();
		checkReExportState();
		return;
	}

	// check if the directory is valid
	KURL url(m_data->m_generator_lasturl);
	if (!url.isValid() || !url.isLocalFile() || !QFile::exists(m_data->m_generator_lasturl+"/.kdissert"))
	{
		KMessageBox::sorry(m_canvasview, i18n("Failed to re-create a document at %1").arg(m_data->m_generator_lasturl) );
		m_data->m_generator_lastgen.clear();
		checkReExportState();
		return;
	}

	// read last generation date (for backup suffix) and save the actual one
	std::string hash;
	std::ifstream is(m_data->m_generator_lasturl+"/.kdissert");
	is>>hash;
	is.close();
	std::ofstream os(m_data->m_generator_lasturl+"/.kdissert");
	os<<QDate::currentDate().toString(Qt::ISODate)
		<<'-'
		<<QTime::currentTime().toString(Qt::ISODate);
	
	// check if the directory exists
	QFileInfo fileinfo(m_data->m_generator_lasturl);
	if (!fileinfo.isDir() || !fileinfo.isWritable())
	{
		KMessageBox::sorry(m_canvasview, i18n("Location %1 is not accessible anymore").arg(m_data->m_generator_lasturl) );
		m_data->m_generator_lastgen.clear();
		checkReExportState();
		return;
	}

	for (QStringList::iterator it(m_data->m_generator_lastgen.begin()); it!=m_data->m_generator_lastgen.end(); ++it)
	{
		// load the library
		QString libname = QString("lib")+*it;
		KLibFactory* factory = KLibLoader::self()->factory("lib"+*it);
		if (!factory)
		{
			KMessageBox::sorry(m_canvasview, i18n("Generator %1 is not accessible anymore").arg(*it));
			m_data->m_generator_lastgen.clear();
			checkReExportState();
			continue;
		}
		DGenerator* generator = static_cast<DGenerator*> (factory->create(this, "Thanks", "Linus"));
		if (!generator)
		{
			KMessageBox::sorry(m_canvasview, i18n("Generator %1 will not work - please check your installation").arg(*it) );
			m_data->m_generator_lastgen.clear();
			checkReExportState();
			continue;
		}

		QDir dir(m_data->m_generator_lasturl+"/"+*it);
		if (dir.exists())
			if (Settings::backupGenerated())
			{
				if (!dir.rename(m_data->m_generator_lasturl+"/"+*it, m_data->m_generator_lasturl+"/"+*it+"."+hash+".old"))
				{
					KMessageBox::sorry(m_canvasview, i18n("Cannot move previously generated directory %1, abort generation.").arg(dir.dirName()));
					return;
				}
			}
			else // remove directory
				for (QStringList todelete(dir.absPath()); todelete.count()>0;)
					if (QRegExp(".*/\\.{1,2}").exactMatch(todelete.first()))
						todelete.pop_front(); // ignore . and ..
					else
						if (QFileInfo(todelete.first()).isDir()) // is a directory
							if (!dir.rmdir(todelete.first())) // try to remove (works if empty), or add contents to deletion list
								todelete = QDir(todelete.front()).entryList().gres(QRegExp("^(.*)$"), todelete.front()+"/\\1") + todelete;
							else // deletion was successful
								todelete.pop_front();
						else // it is a file (or link or whatsoever)
							if (!dir.remove(todelete.front())) { // removing it must work
								KMessageBox::sorry(m_canvasview, i18n("Cannot delete file %1.").arg(todelete.front()));
								return;
							} else // deletion was successful
								todelete.pop_front();

		connect(generator, SIGNAL(documentGenerated(const QString&, bool)), this, SLOT(launchURL(const QString&, bool)) );
		generator->generate( m_data->m_generator_lasturl+"/"+*it, m_data );
		disconnect(generator, SIGNAL(documentGenerated(const QString&, bool)), this, SLOT(launchURL(const QString&, bool)) );
	}
	
	// TODO : it would be better if we put all old archives into one directory
	//QDir dir2( m_data->m_generator_lasturl+extension );
	//dir2.rename( dir2.absPath(), m_data->m_generator_lasturl+"/"+dir2.dirName() );
}

void KDissertPart::generateDocument()
{
	if (!m_data) return;

	if (!m_data->canGenerate())
	{
		KMessageBox::sorry(m_canvasview,
				i18n("Your document does not have any root connected to children\n"
					"kdissert will not generate any document."),
				i18n("Invalid Document") );
		return;
	}

	QStringList lst = KGlobal::instance()->dirs()->findAllResources("module", "*kdiss*.la");
	//kdWarning()<<" *** kdissert generators found *** \n"<<lst.join("\n")<<endl;

	// load the libraries
	QValueList<DGenerator*> liblist;
	for (unsigned int i=0; i<lst.count(); i++)
	{
		QString libname = lst[i].section( '/', -1 );

		libname.truncate(libname.length()-3);
		//kdWarning()<<"loading : "<<libname<<endl;

		KLibFactory* factory = KLibLoader::self()->factory(libname);
		if (factory)
		{
			//kdWarning()<<"library successfully dlopened!!"<<endl;
			DGenerator* lib = static_cast<DGenerator*> (factory->create(this, "alpha", "omega"));
			//kdWarning()<<"library full name is  : "<<lib->fullName()<<endl;
			//kdWarning()<<"library group is : "<<lib->group()<<endl;
			liblist.push_back(lib);
		}
		else
		{
			kdWarning()<<"library "<<lst[i]<<" is invalid "<<KLibLoader::self()->lastErrorMessage()<<endl;
		}
	}

	if (liblist.count() > 0)
	{
		generatorwizard *wiz = new generatorwizard(this);
		wiz->setgeneratorlist(liblist);
		wiz->setgeneratordata(m_data);
		wiz->exec();

		delete wiz;

		checkReExportState();
	}
	else
	{
		KMessageBox::sorry(m_canvasview, i18n("No templates were found, please check your installation."), 
				i18n("Templates Are Missing") );
	}

	// clean after use
	for (unsigned int i=0; i<liblist.count(); i++)
	{
		delete liblist[i];
	}
	KLibLoader::self()->cleanUp();
}

void KDissertPart::slotPrintMap()
{
	m_canvasview->deselectAll();
	QCanvas *canvas = m_canvasview->canvas();
	QRect enclosingsize = m_canvasview->canvasSize();
	KPrinter pr;

	if ( pr.setup() ) 
	{
		QPainter p(&pr);

		// This is to scale the image, so that it always fits to one page.
		QPaintDeviceMetrics pdm(&pr);
		double scale_x = ((double) pdm.width())/enclosingsize.width();
		double scale_y = ((double) pdm.height())/enclosingsize.height();
		double scale = (scale_x<scale_y)?scale_x:scale_y;
		p.scale(scale, scale);

		// Move the view so that it is in the middle
		p.translate(-enclosingsize.x() + 
				(pdm.width() / scale - enclosingsize.width()) / 2.,
				-enclosingsize.y() + 
				(pdm.height() / scale - enclosingsize.height()) / 2.);

		// And draw the area
		canvas->drawArea(enclosingsize, &p);
	}
}

void KDissertPart::savePic()
{
	if (!m_data) return;

	m_canvasview->deselectAll();
	KURL url = KFileDialog::getSaveURL(QString::null, i18n("*.png|PNG Files (*.png)"), 
			m_canvasview, i18n("Grab Picture of Mindmap"));

	if (url.isEmpty() || !url.isValid())
	{
		//kdWarning()<<"invalid url !"<<endl;
		return;
	}
	savePicURL(url);
}

void KDissertPart::savePicURL(const KURL& url)
{
	QCanvas * canvas = m_canvasview->canvas();

	QRect enclosingsize = m_canvasview->canvasSize();

	QPixmap pix(enclosingsize.width(), enclosingsize.height());
	QPainter p(&pix);
	p.translate(-enclosingsize.x(), -enclosingsize.y());
	canvas->drawArea( enclosingsize, &p );

	KTempFile tf;
	tf.close();
	tf.setAutoDelete(true);

	pix.save(tf.name(), "PNG");
	KIO::NetAccess::upload(tf.name(), url, NULL);
}

void KDissertPart::setActionType( int act )
{
	switch (act)
	{
		case DCanvasView::act_point:
			m_select->setChecked(true);
			break;
		case DCanvasView::act_link:
			m_link->setChecked(true);
			break;
		case DCanvasView::act_sort:
			m_sort->setChecked(true);
			break;
		case DCanvasView::act_scroll:
			m_scroll->setChecked(true);
			break;
		default:
			kdWarning()<<"KDissertPart::setActionType unknown action type "<<act<<endl;
	}
}

void KDissertPart::setLinkMode()
{
	m_canvasview->setActionType( DCanvasView::act_link );
}

void KDissertPart::setPointMode()
{
	m_canvasview->setActionType( DCanvasView::act_point );
}

void KDissertPart::setSortMode()
{
	m_canvasview->setActionType( DCanvasView::act_sort );
}

void KDissertPart::setScrollMode()
{
	m_canvasview->setActionType( DCanvasView::act_scroll );
}

void KDissertPart::itemChanged(int val, DGuiView*)
{
	m_lastid = val;
	DDataItem* item = (DDataItem*) m_data->Item(val);
	emit itemSelected( item, false );

	if (item)
	{
		m_warning->setEnabled(true);
		m_idea->setEnabled(true);
		m_good->setEnabled(true);
		m_work->setEnabled(true);
		m_clarify->setEnabled(true);
		m_question->setEnabled(true);
		m_trash->setEnabled(true);
		m_meeting->setEnabled(true);
		m_trouble->setEnabled(true);

		
		m_warning->setChecked( (bool) item->hasFlag(DDataControl::e_warning) );
		m_good->setChecked( (bool) item->hasFlag(DDataControl::e_good) );
		m_idea->setChecked( (bool) item->hasFlag(DDataControl::e_idea) );
		m_work->setChecked( (bool) item->hasFlag(DDataControl::e_work) );
		m_clarify->setChecked( (bool) item->hasFlag(DDataControl::e_clarify) );
		m_question->setChecked( (bool) item->hasFlag(DDataControl::e_question) );
		m_trash->setChecked( (bool) item->hasFlag(DDataControl::e_trash) );
		m_meeting->setChecked( (bool) item->hasFlag(DDataControl::e_meeting) );
		m_trouble->setChecked( (bool) item->hasFlag(DDataControl::e_trouble) );
	}
	else
	{
		m_warning->setEnabled(false);
		m_idea->setEnabled(false);
		m_good->setEnabled(false);
		m_work->setEnabled(false);
		m_clarify->setEnabled(false);
		m_question->setEnabled(false);
		m_trash->setEnabled(false);
		m_meeting->setEnabled(false);
		m_trouble->setEnabled(false);

		m_warning->setChecked(false);
		m_idea->setChecked(false);
		m_good->setChecked(false);
		m_work->setChecked(false);
		m_clarify->setChecked(false);
		m_question->setChecked(false);
		m_trash->setChecked(false);
		m_meeting->setChecked(false);
		m_trouble->setChecked(false);
	}
}

void KDissertPart::checkFlags()
{
	DDataItem* item = (DDataItem*) m_data->Item(m_lastid);
	if (!item) return;

	item->setFlag( DDataControl::e_warning, m_warning->isChecked() );
	item->setFlag( DDataControl::e_good, m_good->isChecked() );
	item->setFlag( DDataControl::e_idea, m_idea->isChecked() );
	item->setFlag( DDataControl::e_work, m_work->isChecked() );
	item->setFlag( DDataControl::e_clarify, m_clarify->isChecked() );
	item->setFlag( DDataControl::e_question, m_question->isChecked() );
	item->setFlag( DDataControl::e_trash, m_trash->isChecked() );
	item->setFlag( DDataControl::e_meeting, m_meeting->isChecked() );
	item->setFlag( DDataControl::e_trouble, m_trouble->isChecked() );
	
	m_data->notifyChildChange(m_lastid);
}

void KDissertPart::applyColors()
{
	int color = Settings::EnumColorMode::default_;
	if (m_theme1->isChecked()) color = Settings::EnumColorMode::theme1_;
	else if (m_theme2->isChecked()) color = Settings::EnumColorMode::theme2_;
	else if (m_theme3->isChecked()) color = Settings::EnumColorMode::theme3_;
	else if (m_theme4->isChecked()) color = Settings::EnumColorMode::theme4_;
	else if (m_theme5->isChecked()) color = Settings::EnumColorMode::theme5_;
	else if (m_theme6->isChecked()) color = Settings::EnumColorMode::theme6_;
	else if (m_custom->isChecked())
	{
		m_canvasview->applyCustomColor();
		m_custom->setChecked(false);
		m_treelistview->triggerUpdate();
		return;
	}
	m_canvasview->applyColorScheme(color);
	m_treelistview->triggerUpdate();
}

void KDissertPart::updateColorActionsState()
{
	if (m_canvasview->itemsAreSelected())
	{
		m_default->setEnabled(true);
		m_theme1->setEnabled(true);
		m_theme2->setEnabled(true);
		m_theme3->setEnabled(true);
		m_theme4->setEnabled(true);
		m_theme5->setEnabled(true);
		m_theme6->setEnabled(true);
		m_custom->setEnabled(true);

		if (m_canvasview->oneItemIsSelected())
		{
			DDataItem *item = m_data->singleSelection();
			switch (item->colorScheme())
			{
				case 1:
					m_default->setChecked(true);
					break;
				case 2:
					m_theme1->setChecked(true);
					break;
				case 3:
					m_theme2->setChecked(true);
					break;
				case 4:
					m_theme3->setChecked(true);
					break;
				case 5:
					m_theme4->setChecked(true);
					break;
				case 6:
					m_theme5->setChecked(true);
					break;
				case 7:
					m_theme6->setChecked(true);
					break;
				default:
					break;
			}
		}
		else
		{
			m_default->setChecked(false);
			m_theme1->setChecked(false);
			m_theme2->setChecked(false);
			m_theme3->setChecked(false);
			m_theme4->setChecked(false);
			m_theme5->setChecked(false);
			m_theme6->setChecked(false);
			m_custom->setChecked(false);
		}
	}
	else
	{
		m_default->setEnabled(false);
		m_theme1->setEnabled(false);
		m_theme2->setEnabled(false);
		m_theme3->setEnabled(false);
		m_theme4->setEnabled(false);
		m_theme5->setEnabled(false);
		m_theme6->setEnabled(false);
		m_custom->setEnabled(false);

		m_default->setChecked(false);
		m_theme1->setChecked(false);
		m_theme2->setChecked(false);
		m_theme3->setChecked(false);
		m_theme4->setChecked(false);
		m_theme5->setChecked(false);
		m_theme6->setChecked(false);
		m_custom->setChecked(false);
	}
}

void KDissertPart::setItemChanged(int id)
{
	emit itemChanged(id);
}

void KDissertPart::setZoom(float zoom)
{
	m_zoom = zoom;
	QWMatrix wm;
	wm.setMatrix(zoom, 0, 0, zoom, 0, 0);
	m_canvasview->setWorldMatrix( wm );
	m_canvasview->focusOnRoot();
}

float KDissertPart::getZoom()
{
	return m_zoom;
}

void KDissertPart::docProperties()
{
	docsettingsdlg* dlg = new docsettingsdlg(this->widget(), m_data);
	dlg->exec();
}

void KDissertPart::launchURL(const QString& url, bool usekfmclient)
{
	QMap<QChar,QString> map;
	map.insert('s', url);

	QString command = Settings::launchCommand();
	if (usekfmclient) command = "kfmclient exec %s";
	
	KRun::runCommand( KMacroExpander::expandMacrosShellQuote( command, map ) );
}

void KDissertPart::focusOut()
{
	if (m_zoom35->isChecked()) m_zoom20->activate();
	if (m_zoom50->isChecked()) m_zoom35->activate();
	if (m_zoom75->isChecked()) m_zoom50->activate();
	else if (m_zoom100->isChecked()) m_zoom75->activate();
	else if (m_zoom125->isChecked()) m_zoom100->activate();
	else if (m_zoom150->isChecked()) m_zoom125->activate();
	else if (m_zoom200->isChecked()) m_zoom150->activate();
}

void KDissertPart::focusIn()
{
	if (m_zoom20->isChecked()) m_zoom35->activate();
	else if (m_zoom35->isChecked()) m_zoom50->activate();
	else if (m_zoom50->isChecked()) m_zoom75->activate();
	else if (m_zoom75->isChecked()) m_zoom100->activate();
	else if (m_zoom100->isChecked()) m_zoom125->activate();
	else if (m_zoom125->isChecked()) m_zoom150->activate();
	else if (m_zoom150->isChecked()) m_zoom200->activate();
}

void KDissertPart::focus20()  { setZoom(0.2); m_zoom20->setChecked(true); }
void KDissertPart::focus35()  { setZoom(0.35); m_zoom35->setChecked(true); }
void KDissertPart::focus50()  { setZoom(0.50); m_zoom50->setChecked(true); }
void KDissertPart::focus75()  { setZoom(0.75); m_zoom75->setChecked(true); }
void KDissertPart::focus100() { setZoom(1.0); m_zoom100->setChecked(true); }
void KDissertPart::focus125() { setZoom(1.25); m_zoom125->setChecked(true); }
void KDissertPart::focus150() { setZoom(1.5); m_zoom150->setChecked(true); }
void KDissertPart::focus200() { setZoom(2.0); m_zoom200->setChecked(true); }

void KDissertPart::settingsChanged()
{
	// refresh the pixmaps
	for (int id=0; id<7; id++)
	{
		for (int state=0; state<3; state++)
		{
			// loop on both states and identifiers
			int keymul = (state+1) + 100*id;
			QPixmapCache::remove( QString("kdi")+QString::number(keymul) );
		}
	}

	m_data->updateSettings();
	updateColorActions();
}

class MyIconFactory : public QIconFactory
{
	public:
		MyIconFactory(int id, int size);
		virtual QPixmap* createPixmap( const QIconSet&, QIconSet::Size, QIconSet::Mode, QIconSet::State );
	private:
		int m_id;
		int m_size;
};

MyIconFactory::MyIconFactory(int id, int size)
{
	m_id = id;
	m_size = size;
	setAutoDelete(true);
}

QPixmap* MyIconFactory::createPixmap(const QIconSet& set, QIconSet::Size, QIconSet::Mode mode_P, QIconSet::State )
{
	// borrrowed from kdelibs
	// QIconSet::Mode to KIcon::State conversion
	static const KIcon::States tbl[] = { KIcon::DefaultState, KIcon::DisabledState, KIcon::ActiveState };
	int state = KIcon::DefaultState;
	if ( mode_P <= QIconSet::Active )
		state = tbl[ mode_P ];
	
	int keymul = (state+1) + 100*m_id; // quick hash function
	QPixmap *pix = QPixmapCache::find( QString("kdi")+QString::number(keymul) );
	if (pix) return new QPixmap(*pix);
	
	QPixmap tmppix(m_size, m_size);
	tmppix.fill( Settings::fillcolor( m_id ) );

	QPainter p(&tmppix);
	QPen pen;
	pen.setColor( Settings::textcolor( m_id ) );
	p.setPen(pen);

	int s = m_size; // more readable
	p.drawLine(1, 1, s-2, 1);
	p.drawLine(1, s-2, s-2, s-2);
	p.drawLine(1, 1, 1, s-2);
	p.drawLine(s-2, 1, s-2, s-2);
	
	KIconEffect *effect = KGlobal::iconLoader()->iconEffect();
	QPixmap newpix = effect->apply(tmppix, KIcon::Toolbar, state);
	QPixmapCache::insert( QString("kdi")+QString::number(keymul), newpix );

	return new QPixmap(newpix);
}

void setColorAction(KRadioAction *act, int colorid)
{
	int s = KIcon::SizeSmall;
	QIconSet iconset;
	iconset.installIconFactory( new MyIconFactory(colorid, s) );
	act->setIconSet(iconset);
}

void KDissertPart::updateColorActions()
{
	if (Settings::annotation(Settings::EnumColorMode::theme1_).length())
		m_theme1->setText(Settings::annotation(Settings::EnumColorMode::theme1_));
	else
		m_theme1->setText(i18n("Theme 1"));
	if (Settings::annotation(Settings::EnumColorMode::theme2_).length())
		m_theme2->setText(Settings::annotation(Settings::EnumColorMode::theme2_));
	else
		m_theme2->setText(i18n("Theme 2"));
	if (Settings::annotation(Settings::EnumColorMode::theme3_).length())
		m_theme3->setText(Settings::annotation(Settings::EnumColorMode::theme3_));
	else
		m_theme3->setText(i18n("Theme 3"));
	if (Settings::annotation(Settings::EnumColorMode::theme4_).length())
		m_theme4->setText(Settings::annotation(Settings::EnumColorMode::theme4_));
	else
		m_theme4->setText(i18n("Theme 4"));
	if (Settings::annotation(Settings::EnumColorMode::theme5_).length())
		m_theme5->setText(Settings::annotation(Settings::EnumColorMode::theme5_));
	else
		m_theme5->setText(i18n("Theme 5"));
	if (Settings::annotation(Settings::EnumColorMode::theme6_).length())
		m_theme6->setText(Settings::annotation(Settings::EnumColorMode::theme6_));
	else
		m_theme6->setText(i18n("Theme 6"));

	setColorAction(m_default, Settings::EnumColorMode::default_);
	setColorAction(m_theme1, Settings::EnumColorMode::theme1_);
	setColorAction(m_theme2, Settings::EnumColorMode::theme2_);
	setColorAction(m_theme3, Settings::EnumColorMode::theme3_);
	setColorAction(m_theme4, Settings::EnumColorMode::theme4_);
	setColorAction(m_theme5, Settings::EnumColorMode::theme5_);
	setColorAction(m_theme6, Settings::EnumColorMode::theme6_);
}

void KDissertPart::guiActivateEvent( KParts::GUIActivateEvent* event )
{
	KParts::ReadWritePart::guiActivateEvent( event );
	if (event->activated())
	{
		updateColorActions();
		updateColorActionsState();
	}
}

void KDissertPart::setUndoState(bool b)
{
	//kdWarning()<<"KDissertPart::setUndoState"<<endl;
	m_undo->setEnabled(b);
	slotRefresh();
}

void KDissertPart::setRedoState(bool b)
{
	//kdWarning()<<"KDissertPart::setRedoState"<<endl;
	m_redo->setEnabled(b);
	slotRefresh();
}

void KDissertPart::slotRefresh()
{
	m_canvasview->canvas()->setAllChanged();
	m_treelistview->triggerUpdate();
}

void KDissertPart::emitStatusBarMessage(const QString &txt)
{
	emit setStatusBarText(txt);
}

void KDissertPart::importData()
{
        KURL url = KFileDialog::getOpenURL(QString::null,
                        i18n("*.kdi|kdissert Project (*.kdi)"),
                        m_canvasview, i18n("Open Mindmap Project"));

        if (! url.isValid()) return;

        bool result = m_data->loadFromFile(url, true);
        if (result)
        {
                m_canvasview->focusOnRoot();
                setModified( true );
        }
}

DCanvasView* KDissertPart::getCanvas()
{
	return m_canvasview;
}

#include "KDissertPart.moc"
