#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>

#include <tqpopupmenu.h>
#include <tqfile.h>
#include <tqlayout.h>
#include <tqmap.h>
#include <tqlabel.h>
#include <tqradiobutton.h>
#include <tqcheckbox.h>
#include <tqdom.h>

#include <kmimetype.h>
#include <kservice.h>
#include <ktrader.h>
#include <tdeapplication.h>
#include <krun.h>
#include <kdebug.h>
#include <tdelocale.h>
#include <tdemessagebox.h>
#include <tdeparts/part.h>
#include <tdeparts/factory.h>
#include <tdeparts/partmanager.h>
#include <tdeparts/browserextension.h>
#include <tdefiledialog.h>
#include <tdemainwindow.h>
#include <tdeaction.h>
#include <tdemessagebox.h>
#include <kstatusbar.h>
#include <tdehtml_part.h>
#include <tdepopupmenu.h>
#include <tdeio/netaccess.h>
#include <kdialogbase.h>
#include <klineedit.h>
#include <tdeshortcut.h>
#include <kcompletion.h>
#include <kdirwatch.h>
#include <tdeversion.h>
#include <kiconloader.h>
#include <kuserprofile.h>
#include <kencodingfiledialog.h>
#include <ksqueezedtextlabel.h>

#include <tdetexteditor/view.h>
#include <tdetexteditor/document.h>
#include <tdetexteditor/viewcursorinterface.h>
#include <tdetexteditor/encodinginterface.h>

#include "toplevel.h"
#include "api.h"
#include "core.h"
#include "editorproxy.h"
#include "documentationpart.h"
#include "ksavealldialog.h"

#include "kdevproject.h"
#include "urlutil.h"
#include "mimewarningdialog.h"
#include "domutil.h"
#include "kdevjobtimer.h"

#include "designer.h"
#include "kdevlanguagesupport.h"

#include "multibuffer.h"
#include "partcontroller.h"


class TQDomDocument;

PartController *PartController::s_instance = 0;

using namespace MainWindowUtils;

struct HistoryEntry
{
    KURL url;
    TQString context;

    HistoryEntry( const KURL& u, const TQString& c ): url( u ), context( c ) {}
};

struct ModificationData
{
	KTextEditor::Document * doc;
	bool isModified;
	unsigned char reason;
};


PartController::PartController(TQWidget *parent)
  : KDevPartController(parent), _editorFactory(0L), m_currentActivePart(0), m_removingActivePart(false)
{
  connect(this, TQT_SIGNAL(partRemoved(KParts::Part*)), this, TQT_SLOT(slotPartRemoved(KParts::Part* )) );
  connect(this, TQT_SIGNAL(partAdded(KParts::Part*)), this, TQT_SLOT(slotPartAdded(KParts::Part* )) );
  connect(this, TQT_SIGNAL(activePartChanged(KParts::Part*)), this, TQT_SLOT(slotActivePartChanged(KParts::Part*)));

  setupActions();

  m_isJumping = false;

  m_openNextAsText = false;
}


PartController::~PartController()
{
}


void PartController::createInstance(TQWidget *parent)
{
  if (!s_instance)
    s_instance = new PartController(parent);
}


PartController *PartController::getInstance()
{
  return s_instance;
}


void PartController::setupActions()
{
  TDEActionCollection *ac = TopLevel::getInstance()->main()->actionCollection();

  TDEAction* newAction = KStdAction::open(this, TQT_SLOT(slotOpenFile()), ac, "file_open");
  newAction->setToolTip( i18n("Open file") );
  newAction->setWhatsThis( i18n("<b>Open file</b><p>Opens an existing file without adding it to the project.</p>") );

  m_openRecentAction = KStdAction::openRecent( this, TQT_SLOT(slotOpenRecent(const KURL&) ), ac, "file_open_recent" );
  m_openRecentAction->setWhatsThis(TQString("<b>%1</b><p>%2").arg(beautifyToolTip(m_openRecentAction->text())).arg(i18n("Opens recently opened file.")));
  m_openRecentAction->loadEntries( kapp->config(), "RecentFiles" );

  m_saveAllFilesAction = new TDEAction(i18n("Save Al&l"), 0, this, TQT_SLOT(slotSaveAllFiles()), ac, "file_save_all");
  m_saveAllFilesAction->setToolTip( i18n("Save all modified files") );
  m_saveAllFilesAction->setWhatsThis(i18n("<b>Save all</b><p>Saves all modified files."));
  m_saveAllFilesAction->setEnabled(false);

  m_revertAllFilesAction = new TDEAction(i18n("Rever&t All"), 0, this, TQT_SLOT(slotRevertAllFiles()), ac, "file_revert_all");
  m_revertAllFilesAction->setToolTip(i18n("Revert all changes"));
  m_revertAllFilesAction->setWhatsThis(i18n("<b>Revert all</b><p>Reverts all changes in opened files. Prompts to save changes so the reversion can be canceled for each modified file."));
  m_revertAllFilesAction->setEnabled(false);

  m_closeWindowAction = KStdAction::close(this, TQT_SLOT(slotCloseWindow()), ac, "file_close");
  m_closeWindowAction->setToolTip( i18n("Close current file") );
  m_closeWindowAction->setWhatsThis(TQString("<b>%1</b><p>%2").arg(beautifyToolTip(m_closeWindowAction->text())).arg(i18n("Closes current file.")));
  m_closeWindowAction->setEnabled(false);

  m_closeAllWindowsAction = new TDEAction(i18n("Close All"), 0, this, TQT_SLOT(slotCloseAllWindows()), ac, "file_close_all");
  m_closeAllWindowsAction->setToolTip( i18n("Close all files") );
  m_closeAllWindowsAction->setWhatsThis(i18n("<b>Close all</b><p>Close all opened files."));
  m_closeAllWindowsAction->setEnabled(false);

  m_closeOtherWindowsAction = new TDEAction(i18n("Close All Others"), 0, this, TQT_SLOT(slotCloseOtherWindows()), ac, "file_closeother");
  m_closeOtherWindowsAction->setToolTip( i18n("Close other files") );
  m_closeOtherWindowsAction->setWhatsThis(i18n("<b>Close all others</b><p>Close all opened files except current."));
  m_closeOtherWindowsAction->setEnabled(false);

  new TDEActionSeparator(ac, "dummy_separator");

  m_backAction = new TDEToolBarPopupAction(i18n("Back"), "back", 0, this, TQT_SLOT(slotBack()), ac, "history_back");
  m_backAction->setEnabled( false );
  m_backAction->setToolTip(i18n("Back"));
  m_backAction->setWhatsThis(i18n("<b>Back</b><p>Moves backwards one step in the navigation history."));
  connect(m_backAction->popupMenu(), TQT_SIGNAL(aboutToShow()), this, TQT_SLOT(slotBackAboutToShow()));
  connect(m_backAction->popupMenu(), TQT_SIGNAL(activated(int)), this, TQT_SLOT(slotBackPopupActivated(int)));

  m_forwardAction = new TDEToolBarPopupAction(i18n("Forward"), "forward", 0, this, TQT_SLOT(slotForward()), ac, "history_forward");
  m_forwardAction->setEnabled( false );
  m_forwardAction->setToolTip(i18n("Forward"));
  m_forwardAction->setWhatsThis(i18n("<b>Forward</b><p>Moves forward one step in the navigation history."));
  connect(m_forwardAction->popupMenu(), TQT_SIGNAL(aboutToShow()), this, TQT_SLOT(slotForwardAboutToShow()));
  connect(m_forwardAction->popupMenu(), TQT_SIGNAL(activated(int)), this, TQT_SLOT(slotForwardPopupActivated(int)));

  m_gotoLastEditPosAction = new TDEAction( i18n("Goto Last Edit Position"), "go-bottom", 0, this, TQT_SLOT(gotoLastEditPos()), ac, "goto_last_edit_pos" );
  m_gotoLastEditPosAction->setEnabled( false );
  m_gotoLastEditPosAction->setToolTip( i18n("Goto Last Edit Position") );
  m_gotoLastEditPosAction->setWhatsThis( i18n("<b>Goto Last Edit Position</b><p>Open the last edited file and position cursor at the point of edit") );
}

void PartController::setEncoding(const TQString &encoding)
{
  m_presetEncoding = encoding;
}

KParts::Part* PartController::findOpenDocument(const KURL& url)
{
	// if we find it this way, all is well
	KParts::Part * part = partForURL( url );
	if ( part )
	{
		return part;
	}

	// ok, let's see if we can try harder
	if ( API::getInstance()->project() )
	{
		KURL partURL = findURLInProject( url );
		partURL.cleanPath();
		return partForURL( partURL );
	}

	return 0L;
}

KURL PartController::findURLInProject(const KURL& url)
{
  TQStringList fileList = API::getInstance()->project()->allFiles();

  bool filenameOnly = (url.url().find('/') == -1);
  TQString filename = filenameOnly ? "/" : "";
  filename += url.url();

  for (TQStringList::Iterator it = fileList.begin(); it != fileList.end(); ++it) {
    if ((*it).endsWith(filename)) {
      // Match! The first one is as good as any one, I guess...
      return KURL( API::getInstance()->project()->projectDirectory() + "/" + *it );
    }
  }

  return url;
}

void PartController::editDocument(const KURL &inputUrl, int lineNum, int col)
{
  editDocumentInternal(inputUrl, lineNum, col);
}

void PartController::splitCurrentDocument(const KURL &inputUrl,
                                          int lineNum, int col)
{
  editDocumentInternal(inputUrl, lineNum, col, true, true);
}

void PartController::scrollToLineColumn(const KURL &inputUrl,
                                        int lineNum, int col, bool storeHistory )
{
    if ( KParts::ReadOnlyPart *existingPart = partForURL( inputUrl ) )
    {
        if( storeHistory ) addHistoryEntry( existingPart );
        EditorProxy::getInstance()->setLineNumber( existingPart, lineNum, col );
        return;
    }
}

void PartController::editDocumentInternal( const KURL & inputUrl, int lineNum,
                                           int col, bool activate,
                                           bool addToCurrentBuffer )
{
    kdDebug(9000) << k_funcinfo << "\n " << inputUrl.prettyURL()
        << " linenum " << lineNum << " activate? " << activate
        << " addToCurrentBuffer? " << addToCurrentBuffer << endl;

	KURL url = inputUrl;

	// is it already open?
	// (Try this once before verifying the URL, we could be dealing with a file that no longer exists on disc)
	if ( KParts::Part *existingPart = partForURL( url ) )
	{
		// if we've been asked to OpenAs an open file with a specific encoding, assume the user wants to change encoding
		if ( !m_presetEncoding.isNull() )
		{
			if ( KTextEditor::EncodingInterface * ei = dynamic_cast<KTextEditor::EncodingInterface*>( existingPart ) )
			{
				ei->setEncoding( m_presetEncoding );
			}
			m_presetEncoding = TQString();
		}

		addHistoryEntry();
		activatePart( existingPart );
    	EditorProxy::getInstance()->setLineNumber( existingPart, lineNum, col );
		return;
	}

	// Make sure the URL exists
	if ( !url.isValid() || !TDEIO::NetAccess::exists(url, false, 0) )
	{
		bool done = false;

		// Try to find this file in the current project's list instead
		if ( API::getInstance()->project() )
		{
			if (url.isRelativeURL(url.url())) {
				KURL relURL(API::getInstance()->project()->projectDirectory());
				relURL.addPath( url.url() );

				kdDebug() << k_funcinfo << "Looking for file in project dir: " << API::getInstance()->project()->projectDirectory() << " url " << url.url() << " transformed to " << relURL.url() << ": " << done << endl;
				if (relURL.isValid() && TDEIO::NetAccess::exists(relURL, false, 0)) {
					url = relURL;
					done = true;
				}
				else {
					KURL relURL(API::getInstance()->project()->buildDirectory());
					relURL.addPath( url.url() );
					kdDebug() << k_funcinfo << "Looking for file in build dir: " << API::getInstance()->project()->buildDirectory() << " url " << url.url() << " transformed to " << relURL.url() << ": " << done << endl;
					if (relURL.isValid() && TDEIO::NetAccess::exists(relURL, false, 0)) {
						url = relURL;
						done = true;
					}
				}
			}

			if (!done) {
				url = findURLInProject(url);

				if ( !url.isValid() || !TDEIO::NetAccess::exists(url, false, 0) )
					// See if this url is relative to the current project's directory
					url = API::getInstance()->project()->projectDirectory() + "/" + url.path();

				else
					done = true;
			}
		}

 		if ( !done && ( !url.isValid() || !TDEIO::NetAccess::exists(url, false, 0) ))
		{
			// Not found - prompt the user to find it?
			kdDebug(9000) << "cannot find URL: " << url.url() << endl;
			return;
		}
	}

  // We now have a url that exists ;)

  // clean it and resolve possible symlink
	url.cleanPath(true);
	if (url.isLocalFile())
	{
		TQString path = url.path();
		path = URLUtil::canonicalPath(path);
		if ( !path.isEmpty() )
			url.setPath(path);
	}

	// is it already open?
	KParts::Part *existingPart = partForURL(url);
	if (existingPart)
	{
		addHistoryEntry();
		activatePart(existingPart);
		EditorProxy::getInstance()->setLineNumber(existingPart, lineNum, col);
		return;
	}

    if ( !addToCurrentBuffer )
    {
        if ( KDevLanguageSupport *lang =
             API::getInstance()->languageSupport() )
        {
            // Let the language part override the addToCurrentBuffer flag
            // if it decides to...
            addToCurrentBuffer = lang->shouldSplitDocument( inputUrl );

            if ( addToCurrentBuffer )
            {
                kdDebug(9000) <<
                    "languagePart() insists addToCurrentBuffer = true" << endl;
                // Set activate = true, otherwise we have hard to fix
                // multi-buffer delayed activation.
                // I'll re-look at this later...
                activate = true;
            }
        }
    }

	KMimeType::Ptr MimeType = KMimeType::findByURL( url );

	kdDebug(9000) << "mimeType = " << MimeType->name() << endl;

	// is the URL pointing to a directory?
	if ( MimeType->is( "inode/directory" ) )
	{
		return;
	}

	if ( !m_presetEncoding.isNull() )
	{
		m_openNextAsText = true;
	}

	TDEConfig *config = kapp->config();
	config->setGroup("General Options");

	// we don't trust KDE with designer files, let's handle it ourselves
	if ( !m_openNextAsText && MimeType->is( "application/x-designer" ) )
	{
		TQString DesignerSetting = config->readEntry( "DesignerSetting", "ExternalDesigner" );
		TQString designerExec = "designer";
		TQStringList designerPluginPaths;
		TQDomDocument* dom = API::getInstance()->projectDom();
		if ( dom != 0 )
		{
			// The global option specifies a fallback if the project
			// has no setting or no project is open. However for TQt4
			// projects we want to use ExternalDesigner in any case.
			if ( DomUtil::readIntEntry( *dom, "/kdevcppsupport/qt/version", 3 ) == 4 )
			{
				designerPluginPaths = DomUtil::readListEntry(*dom, "/kdevcppsupport/qt/designerpluginpaths", "path" );
				DesignerSetting = "ExternalDesigner";
			}

			DesignerSetting = DomUtil::readEntry(*dom, "/kdevcppsupport/qt/designerintegration", DesignerSetting  );
			designerExec = DomUtil::readEntry(*dom, "/kdevcppsupport/qt/designer", designerExec );
		}
		if ( DesignerSetting == "ExternalKDevDesigner" )
		{
			designerExec = "kdevdesigner";
		}
		else if ( DesignerSetting == "EmbeddedKDevDesigner" )
		{
			if ( KParts::ReadOnlyPart *designerPart = qtDesignerPart() )
			{
				addHistoryEntry();
				activatePart(designerPart);
				designerPart->openURL(url);
				return;
			}
			else if ( KParts::Factory * KDevDesignerFactory = static_cast<KParts::Factory*>( KLibLoader::self()->factory( TQFile::encodeName( "libkdevdesignerpart" ) ) ) )
			{
				KParts::ReadWritePart * kdevpart = static_cast<KParts::ReadWritePart*>( KDevDesignerFactory->createPart( TopLevel::getInstance()->main(), 0, 0, 0, "KParts::ReadWritePart"  ) );
				kdevpart->openURL( url );
				addHistoryEntry();
				integratePart( kdevpart, url );
				m_openRecentAction->addURL( url );
				m_openRecentAction->saveEntries( kapp->config(), "RecentFiles" );
				return;
			}
		}

		if( designerPluginPaths.isEmpty() )
			KRun::runCommand( designerExec+" "+url.pathOrURL() );
		else
			KRun::runCommand( "TQT_PLUGIN_PATH=\""+designerPluginPaths.join(":")+"\" "+designerExec+" "+url.pathOrURL() );

		return;
	}

	config->setGroup("General");

	TQStringList texttypeslist = config->readListEntry( "TextTypes" );
	if ( texttypeslist.contains( MimeType->name() ) )
	{
		m_openNextAsText = true;
	}

	bool isText = false;
  	TQVariant v = MimeType->property("X-TDE-text");
     	if (v.isValid())
           isText = v.toBool();
	// is this regular text - open in editor
	if ( m_openNextAsText || isText || MimeType->is( "application/x-zerosize" ) )
	{
        KTextEditor::Editor *editorpart =
            createEditorPart( activate, addToCurrentBuffer, url );
        if ( editorpart )
        {
            if ( m_presetEncoding.isNull() && API::getInstance()->projectDom() )
            {
                TQDomDocument * projectDom = API::getInstance()->projectDom();
                m_presetEncoding = DomUtil::readEntry( *projectDom, "/general/defaultencoding", TQString() );
            }

            if ( !m_presetEncoding.isNull() )
            {
                if ( KTextEditor::EncodingInterface * ei = dynamic_cast<KTextEditor::EncodingInterface*>( editorpart ) )
                {
                    ei->setEncoding( m_presetEncoding );
                }
                m_presetEncoding = TQString();
            }

            addHistoryEntry();

            TQWidget * widget = EditorProxy::getInstance()->topWidgetForPart( editorpart );

            integratePart(editorpart, url, widget, true, activate, addToCurrentBuffer);
            EditorProxy::getInstance()->setLineNumber(editorpart, lineNum, col);

            m_openNextAsText = false;

            m_openRecentAction->addURL( url );
            m_openRecentAction->saveEntries( kapp->config(), "RecentFiles" );

            return;
        }
	}

	// OK, it's not text and it's not a designer file.. let's see what else we can come up with..

	KParts::Factory *factory = 0;
	TQString className;

	TQString services[] = { "KParts/ReadWritePart", "KParts/ReadOnlyPart" };
	TQString classnames[] = { "KParts::ReadWritePart", "KParts::ReadOnlyPart" };
	for (uint i=0; i<2; ++i)
	{
		factory = findPartFactory( MimeType->name(), services[i] );
		if (factory)
		{
			className = classnames[i];
			break;
		}
	}

	kdDebug(9000) << "factory = " << factory << endl;

	if (factory)
	{
		// create the object of the desired class
		KParts::ReadOnlyPart *part = static_cast<KParts::ReadOnlyPart*>( factory->createPart( TopLevel::getInstance()->main(), 0, 0, 0, className.latin1() ) );
		if ( part )
		{
			part->openURL( url );
			addHistoryEntry();

			if ( dynamic_cast<KTextEditor::Editor*>( part ) ) // we can have ended up with a texteditor, in which case need to treat it as such
			{
				integratePart(part, url, part->widget(), true, activate);
				EditorProxy::getInstance()->setLineNumber(part, lineNum, col);
			}
			else
			{
				integratePart( part, url );
			}

			m_openRecentAction->addURL( url );
			m_openRecentAction->saveEntries( kapp->config(), "RecentFiles" );
		}
	}
	else
	{
		MimeWarningDialog dlg;
		dlg.text2->setText( TQString( "<qt><b>%1</b></qt>" ).arg(url.path()));
		dlg.text3->setText( dlg.text3->text().arg(MimeType->name()) );

		if ( dlg.exec() == TQDialog::Accepted )
		{
			if ( dlg.open_with_kde->isChecked() )
			{
				KRun::runURL(url, MimeType->name() );
			}
			else
			{
				if ( dlg.always_open_as_text->isChecked() )
				{
					TDEConfig *config = kapp->config();
					config->setGroup("General");
					TQStringList texttypeslist = config->readListEntry( "TextTypes" );
					texttypeslist << MimeType->name();
					config->writeEntry( "TextTypes", texttypeslist );
				}
				m_openNextAsText = true;
				editDocument( url, lineNum, col );
			}
		}
	}
}


void PartController::showDocument(const KURL &url, bool newWin)
{
  TQString fixedPath = HTMLDocumentationPart::resolveEnvVarsInURL(url.url()); // possibly could env vars
  KURL docUrl(fixedPath);
  kdDebug(9000) << "SHOW: " << docUrl.url() << endl;

  if ( docUrl.isLocalFile() && KMimeType::findByURL(docUrl)->name() != "text/html" ) {
    // a link in a html-file pointed to a local text file - display
    // it in the editor instead of a html-view to avoid uglyness
    editDocument( docUrl );
    return;
  }

  addHistoryEntry();

  HTMLDocumentationPart *part = dynamic_cast<HTMLDocumentationPart*>(activePart());
  if (!part || newWin)
  {
    part = new HTMLDocumentationPart;
    integratePart(part,docUrl);
    connect(part, TQT_SIGNAL(fileNameChanged(KParts::ReadOnlyPart* )),
        this, TQT_SIGNAL(partURLChanged(KParts::ReadOnlyPart* )));
  }
  else
  {
    activatePart(part);
  }
  part->openURL(docUrl);
}

KParts::Factory *PartController::findPartFactory(const TQString &mimeType, const TQString &partType, const TQString &preferredName)
{
  TDETrader::OfferList offers = TDETrader::self()->query(mimeType, TQString("'%1' in ServiceTypes").arg(partType));

  if (offers.count() > 0)
  {
    KService::Ptr ptr = 0;
    // if there is a preferred plugin we'll take it
    if ( !preferredName.isEmpty() ) {
      TDETrader::OfferList::Iterator it;
      for (it = offers.begin(); it != offers.end(); ++it) {
        if ((*it)->desktopEntryName() == preferredName) {
          ptr = (*it);
        }
      }
    }
    // else we just take the first in the list
    if ( !ptr ) {
      ptr = offers.first();
    }
    return static_cast<KParts::Factory*>(KLibLoader::self()->factory(TQFile::encodeName(ptr->library())));
  }

  return 0;
}

KTextEditor::Editor * PartController::createEditorPart( bool activate,
                                                        bool addToCurrentBuffer,
                                                        const KURL &url )
{
    MultiBuffer *multiBuffer = 0;
    if ( addToCurrentBuffer )
    {
        multiBuffer =
            dynamic_cast<MultiBuffer*>(
                    EditorProxy::getInstance()->topWidgetForPart( activePart() )
                                      );
    }
    if ( !multiBuffer )
    {
        kdDebug(9000) << "Creating a new MultiBuffer for "
            << url.fileName() << endl;
        multiBuffer = new MultiBuffer( TopLevel::getInstance()->main() );
    }

    static bool alwaysActivate = true;

    kapp->config()->setGroup("Editor");
    TQString preferred = kapp->config()->readPathEntry("EmbeddedKTextEditor");
    // If we are not using kyzis...
    // Don't create non-wrapped views for now,
    // avoid two paths (== two chances for bad bugs)
    if ( preferred != "kyzispart" )
        alwaysActivate = false;

    KTextEditor::Editor *editorpart =
        dynamic_cast<KTextEditor::Editor*>(multiBuffer->createPart( "text/plain",
            "KTextEditor/Document",
            alwaysActivate | activate ?
            "KTextEditor::Editor" : "KTextEditor::Document",
            preferred
            ));

    if ( url.isValid() )
        editorpart->openURL( url );

    multiBuffer->registerURL( url, editorpart );
    multiBuffer->setDelayedActivation( !activate );
    return editorpart;
}

void PartController::integratePart(KParts::Part *part, const KURL &url,
                                   TQWidget* widget, bool isTextEditor,
                                   bool activate, bool addToCurrentBuffer )
{
  if (!widget) widget = part->widget();

  if (!widget) {
    /// @todo error handling
      kdDebug(9000) << "no widget for this part!!" << endl;
      return; // to avoid later crash
  }

  if ( !addToCurrentBuffer )
    TopLevel::getInstance()->embedPartView(widget, url.fileName(), url.url());

  addPart(part, activate);

  // tell the parts we loaded a document
  KParts::ReadOnlyPart *ro_part = dynamic_cast<KParts::ReadOnlyPart*>(part);
  if ( !ro_part ) return;

  emit loadedFile( ro_part->url() );

  connect( part, TQT_SIGNAL(modifiedOnDisc(Kate::Document*, bool, unsigned char)), this, TQT_SLOT(slotDocumentDirty(Kate::Document*, bool, unsigned char)) );

  // let's get notified when a document has been changed
  connect(part, TQT_SIGNAL(completed()), this, TQT_SLOT(slotUploadFinished()));

  // yes, we're cheating again. this signal exists for katepart's
  // Document object and our HTMLDocumentationPart
//  connect(part, TQT_SIGNAL(fileNameChanged()), this, TQT_SLOT(slotFileNameChanged()));

  // Connect to the document's views newStatus() signal in order to keep track of the
  // modified-status of the document.

  if (isTextEditor)
    integrateTextEditorPart(static_cast<KTextEditor::Document*>(part));

  KInterfaceDesigner::Designer *designerPart = dynamic_cast<KInterfaceDesigner::Designer *>(part);
  if (designerPart && API::getInstance()->languageSupport())
  {
      kdDebug() << "integrating designer part with language support" << endl;
      connect(designerPart, TQT_SIGNAL(addedFunction(DesignerType, const TQString&, Function )),
          API::getInstance()->languageSupport(),
          TQT_SLOT(addFunction(DesignerType, const TQString&, Function )));
      connect(designerPart, TQT_SIGNAL(editedFunction(DesignerType, const TQString&, Function, Function )), API::getInstance()->languageSupport(),
      TQT_SLOT(editFunction(DesignerType, const TQString&, Function, Function )));
      connect(designerPart, TQT_SIGNAL(removedFunction(DesignerType, const TQString&, Function )),
          API::getInstance()->languageSupport(),
          TQT_SLOT(removeFunction(DesignerType, const TQString&, Function )));
      connect(designerPart, TQT_SIGNAL(editFunction(DesignerType, const TQString&, const TQString& )),
          API::getInstance()->languageSupport(),
          TQT_SLOT(openFunction(DesignerType, const TQString&, const TQString& )));
      connect(designerPart, TQT_SIGNAL(editSource(DesignerType, const TQString& )),
          API::getInstance()->languageSupport(),
          TQT_SLOT(openSource(DesignerType, const TQString& )));
      connect(designerPart, TQT_SIGNAL(newStatus(const TQString &, int)),
          this, TQT_SLOT(slotNewDesignerStatus(const TQString &, int)));
  }
}

void PartController::integrateTextEditorPart(KTextEditor::Document* doc)
{
  // There's shortcut conflict between Kate and the debugger, resolve
  // it here. Ideally, the should be some standard mechanism, configurable
  // by config files.
  // However, it does not exists and situation here some rare commands
  // like "Dynamic word wrap" or "Show line numbers" from Kate take
  // all possible shortcuts, leaving us with IDE that has no shortcuts for
  // debugger, is very bad.
  //
  // We could try to handle this in debugger, but that would require
  // the debugger to intercept all new KTextEditor::View creations, which is
  // not possible.


  if ( !doc ) return;

  connect( doc, TQT_SIGNAL(textChanged()), this, TQT_SLOT(textChanged()) );
  connect( doc, TQT_SIGNAL(fileNameChanged()),
    this, TQT_SLOT(slotDocumentUrlChanged()));

  if( doc->widget() )
  {
      connect( doc->widget(), TQT_SIGNAL(dropEventPass(TQDropEvent *)),
        TopLevel::getInstance()->main(), TQT_SLOT(slotDropEvent(TQDropEvent *)) );
  }

  if ( KTextEditor::View * view = dynamic_cast<KTextEditor::View*>( doc->widget() ) )
  {
    TDEActionCollection* c = view->actionCollection();
    // Be extra carefull, in case the part either don't provide those
    // action, or uses different shortcuts.

    if (TDEAction* a = c->action("view_folding_markers"))
    {
      if (a->shortcut() == TDEShortcut(Key_F9))
        a->setShortcut(TDEShortcut());
    }

    if (TDEAction* a = c->action("view_dynamic_word_wrap"))
    {
      if (a->shortcut() == TDEShortcut(Key_F10))
        a->setShortcut(TDEShortcut());
    }

    if (TDEAction* a = c->action("view_line_numbers"))
    {
      if (a->shortcut() == TDEShortcut(Key_F11))
        a->setShortcut(TDEShortcut());
    }
  }

  //EditorProxy::getInstance()->installPopup(doc, contextPopupMenu());

  // What's potentially problematic is that this signal isn't officially part of the
  // KTextEditor::View interface. It is nevertheless there, and used in kate and kwrite.
  // There doesn't seem to be any othere way of making this work with katepart, and since
  // signals are dynamic, if we try to connect to an editorpart that lacks this signal,
  // all we get is a runtime warning. At this point in time we are only really supported
  // by katepart anyway so IMHO this hack is justified. //teatime
  TQPtrList<KTextEditor::View> list = doc->views();
  TQPtrListIterator<KTextEditor::View> it( list );
  while ( it.current() )
  {
    connect( it, TQT_SIGNAL( newStatus() ), this, TQT_SLOT( slotNewStatus() ) );
    ++it;
  }
}

void PartController::slotPartAdded( KParts::Part * part )
{
	kdDebug(9000) << k_funcinfo << endl;

	if ( KParts::ReadOnlyPart * ro_part = dynamic_cast<KParts::ReadOnlyPart*>( part ) )
	{
		updatePartURL( ro_part );
	}

	updateMenuItems();
}

void PartController::slotPartRemoved( KParts::Part * part )
{
	kdDebug(9000) << k_funcinfo << endl;

	_partURLMap.remove( static_cast<KParts::ReadOnlyPart*>(part) );

	if ( part == m_currentActivePart )
	{
		m_removingActivePart = true;
	}

	updateMenuItems();
}

void PartController::updatePartURL( KParts::ReadOnlyPart * ro_part )
{
	if ( ro_part->url().isEmpty() )
	{
		kdDebug(9000) << "updatePartURL() called with empty URL for part: " << ro_part << endl;
		return;
	}
	_partURLMap[ ro_part ] = ro_part->url();
}

bool PartController::partURLHasChanged( KParts::ReadOnlyPart * ro_part )
{
	if ( _partURLMap.contains( ro_part ) && !ro_part->url().isEmpty() )
	{
		if ( _partURLMap[ ro_part ] != ro_part->url() )
		{
			return true;
		}
	}
	return false;
}

KURL PartController::storedURLForPart( KParts::ReadOnlyPart * ro_part )
{
	if ( _partURLMap.contains( ro_part ) )
	{
		return _partURLMap[ ro_part ];
	}
	return KURL();
}


void PartController::slotUploadFinished()
{
	KParts::ReadOnlyPart *ro_part = const_cast<KParts::ReadOnlyPart*>( dynamic_cast<const KParts::ReadOnlyPart*>(sender()) );
	if ( !ro_part ) return;

	if ( partURLHasChanged( ro_part ) )
	{
		emit partURLChanged( ro_part );
		updatePartURL( ro_part );
	}
}

KParts::ReadOnlyPart *PartController::partForURL(const KURL &url)
{
	TQPtrListIterator<KParts::Part> it(*parts());
	for ( ; it.current(); ++it)
	{
		KParts::ReadOnlyPart *ro_part = dynamic_cast<KParts::ReadOnlyPart*>(it.current());
		if (ro_part && url.path() == ro_part->url().path())
			return ro_part;
	}
	return 0;
}

KParts::Part * PartController::partForWidget( const TQWidget * widget )
{
	TQPtrListIterator<KParts::Part> it(*parts());
	for ( ; it.current(); ++it)
	{
		if ( it.current()->widget() == widget )
		{
			return *it;
		}
	}
	return 0;
}


void PartController::activatePart(KParts::Part *part)
{
  if ( !part ) return;

  TQWidget * widget = EditorProxy::getInstance()->widgetForPart( part );
  if (widget)
  {
    TopLevel::getInstance()->raiseView( widget );
    widget->show();
    widget->setFocus();
  }

  setActivePart(part);

  TQWidget* w2 = EditorProxy::getInstance()->widgetForPart( part );
  if (w2 != widget)
    w2->setFocus();
}

bool PartController::closePart(KParts::Part *part)
{
    KParts::ReadOnlyPart * ro_part =
        dynamic_cast<KParts::ReadOnlyPart*>( part );

    if ( !ro_part ) return true;

    KURL url = ro_part->url();

    if (TQWidget* w = EditorProxy::getInstance()->topWidgetForPart( part ) )
    {
        if ( MultiBuffer *multiBuffer = dynamic_cast<MultiBuffer*>( w ) )
        {
            kdDebug(9000) << "About to delete MultiBuffered document..."
                << " numberOfBuffers: " << multiBuffer->numberOfBuffers()
                << " isActivated: " << multiBuffer->isActivated()
                << endl;
            if ( !multiBuffer->closeURL( url ) )
                return false;

            if ( multiBuffer->numberOfBuffers() == 0
                 || !multiBuffer->isActivated() )
            {
                TopLevel::getInstance()->removeView( w );
                _dirtyDocuments.remove( static_cast<KParts::ReadWritePart*>( ro_part ) );
                emit closedFile( url );
/*                kdDebug(9000) << "Deleting MultiBuffer Part" << endl;*/
                TopLevel::getInstance()->main()->guiFactory()->removeClient( part );
                delete part;
/*                kdDebug(9000) << "DeleteLater Actual MultiBuffer" << endl;*/
                multiBuffer->deleteLater();
                return true;
            }
            else
            {
/*                kdDebug(9000) << "Deleting MultiBuffer Part" << endl;*/
                _dirtyDocuments.remove( static_cast<KParts::ReadWritePart*>( ro_part ) );
                TopLevel::getInstance()->main()->guiFactory()->removeClient( part );
                emit closedFile( url );
                delete part;
                // Switch to a remaining buffer
                setActivePart( multiBuffer->activeBuffer() );
                return true;
            }
        }
        else if ( !ro_part->closeURL() )
            return false;
	TopLevel::getInstance()->removeView( w );
    }
    else if ( !ro_part->closeURL() )
        return false;
    TopLevel::getInstance()->main()->guiFactory()->removeClient( part );

    _dirtyDocuments.remove( static_cast<KParts::ReadWritePart*>( ro_part ) );
    emit closedFile( url );

/*    kdDebug(9000) << "Deleting Regular Part" << endl;*/
    delete part;
    return true;
}


void PartController::updateMenuItems()
{
  bool hasWriteParts = false;
  bool hasReadOnlyParts = false;

  TQPtrListIterator<KParts::Part> it(*parts());
  for ( ; it.current(); ++it)
  {
    if (it.current()->inherits("KParts::ReadWritePart"))
      hasWriteParts = true;
    if (it.current()->inherits("KParts::ReadOnlyPart"))
      hasReadOnlyParts = true;
  }

  m_saveAllFilesAction->setEnabled(hasWriteParts);
  m_revertAllFilesAction->setEnabled(hasWriteParts);
  m_closeWindowAction->setEnabled(hasReadOnlyParts);
  m_closeAllWindowsAction->setEnabled(hasReadOnlyParts);
  m_closeOtherWindowsAction->setEnabled(hasReadOnlyParts);

  m_backAction->setEnabled( !m_backHistory.isEmpty() );
}

void PartController::slotRevertAllFiles()
{
	revertAllFiles();
}

void PartController::reloadFile( const KURL & url )
{
	KParts::ReadWritePart * part = dynamic_cast<KParts::ReadWritePart*>( partForURL( url ) );
	if ( part )
	{
		if ( part->isModified() )
		{
			if ( KMessageBox::warningYesNo( TopLevel::getInstance()->main(),
				i18n( "The file \"%1\" is modified in memory. Are you sure you want to reload it? (Local changes will be lost.)" ).arg( url.path() ),
				i18n( "File is Modified" ), i18n("Reload"), i18n("Do Not Reload") ) == KMessageBox::Yes )
			{
				part->setModified( false );
			}
			else
			{
				return;
			}
		}

		unsigned int line = 0; unsigned int col = 0;
		KTextEditor::ViewCursorInterface * iface = dynamic_cast<KTextEditor::ViewCursorInterface*>( part->widget() );
		if (iface)
		{
			iface->cursorPositionReal( &line, &col );
		}

		part->openURL( url );

		_dirtyDocuments.remove( part );
		emit documentChangedState( url, Clean );

		if ( iface )
		{
			iface->setCursorPositionReal( line, col );
		}
	}
}

void PartController::revertFiles( const KURL::List & list  )
{
	KURL::List::ConstIterator it = list.begin();
	while ( it != list.end() )
	{
		reloadFile( *it );
		++it;
	}
}

void PartController::revertAllFiles()
{
	revertFiles( openURLs() );
}

void PartController::slotCloseWindow()
{
	closePart( activePart() );
}

KURL::List PartController::modifiedDocuments()
{
	KURL::List modFiles;

	TQPtrListIterator<KParts::Part> it( *parts() );
	while( it.current() )
	{
		KParts::ReadWritePart *rw_part = dynamic_cast<KParts::ReadWritePart*>(it.current());
		if ( rw_part && rw_part->isModified() )
		{
			modFiles << rw_part->url();
		}
		++it;
	}
	return modFiles;
}

void PartController::slotSave()
{
	kdDebug(9000) << k_funcinfo << endl;

	if ( KParts::ReadWritePart * part = dynamic_cast<KParts::ReadWritePart*>( activePart() ) )
	{
		saveFile( part->url() );
	}
}

void PartController::slotReload()
{
	kdDebug(9000) << k_funcinfo << endl;

	if ( KParts::ReadWritePart * part = dynamic_cast<KParts::ReadWritePart*>( activePart() ) )
	{
		reloadFile( part->url() );
	}
}

void PartController::slotSaveAllFiles()
{
  saveAllFiles();
}

bool PartController::saveFile( const KURL & url, bool force )
{
	KParts::ReadWritePart * part = dynamic_cast<KParts::ReadWritePart*>( partForURL( url ) );
	if ( !part ) return true;

	switch( documentState( url ) )
	{
		case Clean:
			if ( !force )
			{
				return true;
			}
			kdDebug(9000) << "Forced save" << endl;
			break;

		case Modified:
			kdDebug(9000) << "Normal save" << endl;
			break;

		case Dirty:
		case DirtyAndModified:
			{
				int code = KMessageBox::warningYesNoCancel( TopLevel::getInstance()->main(),
					i18n("The file \"%1\" is modified on disk.\n\nAre you sure you want to overwrite it? (External changes will be lost.)").arg( url.path() ),
					i18n("File Externally Modified"), i18n("Overwrite"), i18n("Do Not Overwrite") );
				if ( code == KMessageBox::Yes )
				{
					kdDebug(9000) << "Dirty save!!" << endl;
				}
				else if ( code == KMessageBox::No )
				{
					return true;
				}
				else
				{
					return false; // a 'false' return means to interrupt the process that caused the save
				}
			}
			break;

		default:
			;
	}

	if ( part->save() )
	{
		_dirtyDocuments.remove( part );
		emit documentChangedState( url, Clean );
		emit savedFile( url );
	}

	return true;
}

bool PartController::saveAllFiles()
{
	return saveFiles( openURLs() );
}

bool PartController::saveFiles( KURL::List const & filelist )
{
	KURL::List::ConstIterator it = filelist.begin();
	while ( it != filelist.end() )
	{
                if (saveFile( *it )==false)
                   return false; //user cancelled
		++it;
	}
        return true;
}

bool PartController::querySaveFiles()
{
	return saveFilesDialog( KURL::List() );
}

void PartController::clearModified( KURL::List const & filelist )
{
	KURL::List::ConstIterator it = filelist.begin();
	while ( it != filelist.end() )
	{
		KParts::ReadWritePart * rw_part = dynamic_cast<KParts::ReadWritePart*>( partForURL( *it ) );
		if ( rw_part )
		{
			rw_part->setModified( false );
		}
		++it;
	}
}

bool PartController::saveFilesDialog( KURL::List const & ignoreList )
{
	KURL::List modList = modifiedDocuments();

	if ( modList.count() > 0 && modList != ignoreList )
	{
		KSaveSelectDialog dlg( modList, ignoreList, TopLevel::getInstance()->main() );
		if ( dlg.exec() == TQDialog::Accepted )
		{
			saveFiles( dlg.filesToSave() );
			clearModified( dlg.filesNotToSave() );
		}
		else
		{
			return false;
		}
	}
	return true;
}

bool PartController::closeFilesDialog( KURL::List const & ignoreList )
{
	if ( !saveFilesDialog( ignoreList ) ) return false;

	TQPtrList<KParts::Part> partList( *parts() );
	TQPtrListIterator<KParts::Part> it( partList );
	while ( KParts::Part* part = it.current() )
	{
		KParts::ReadOnlyPart * ro_part = dynamic_cast<KParts::ReadOnlyPart*>( part );
		if ( ro_part && !ignoreList.contains( ro_part->url() ) || !ro_part )
		{
			closePart( part );
		}
		++it;
	}
	return true;
}

bool PartController::closeFiles( const KURL::List & list )
{
	KURL::List::ConstIterator it = list.begin();
	while ( it != list.end() )
	{
		if ( !closePart( partForURL( *it ) ) )
		{
			return false;
		}
		++it;
	}
	return true;
}

bool PartController::closeFile( const KURL & url )
{
	return closePart( partForURL( url ) );
}

bool PartController::closeAllFiles()
{
	return closeFilesDialog( KURL::List() );
}

void PartController::slotCloseAllWindows()
{
	closeAllFiles();
}

bool PartController::closeAllOthers( const KURL & url )
{
	KURL::List ignoreList;
	ignoreList.append( url );

	return closeFilesDialog( ignoreList );
}


void PartController::slotCloseOtherWindows()
{
	if ( KParts::ReadOnlyPart * active = dynamic_cast<KParts::ReadOnlyPart*>( activePart() ) )
	{
		closeAllOthers( active->url() );
	}
}

void PartController::slotOpenFile()
{
	TQString DefaultEncoding;
	if ( TQDomDocument * projectDom = API::getInstance()->projectDom() )
	{
		DefaultEncoding = DomUtil::readEntry( *projectDom, "/general/defaultencoding", TQString() );
	}

	if ( DefaultEncoding.isEmpty() )
	{
		// have a peek at katepart's settings:
		TDEConfig * config = kapp->config();
		config->setGroup("Kate Document Defaults");
		DefaultEncoding = config->readEntry("Encoding", TQString() );
	}

	KEncodingFileDialog::Result result = KEncodingFileDialog::getOpenURLsAndEncoding( DefaultEncoding, TQString(),
		TQString(), TopLevel::getInstance()->main(), TQString() );

	for ( KURL::List::Iterator it = result.URLs.begin(); it != result.URLs.end(); ++it )
	{
		m_presetEncoding = result.encoding;
		editDocument( *it );
	}
}

void PartController::slotOpenRecent( const KURL& url )
{
  editDocument( url );
  // stupid bugfix - don't allow an active item in the list
  m_openRecentAction->setCurrentItem( -1 );
}

bool PartController::readyToClose()
{
	blockSignals( true );

	closeAllFiles(); // this should never return false, as the files are already saved

	return true;
}

void PartController::slotActivePartChanged( KParts::Part *part )
{
	kdDebug(9000) << k_funcinfo << part << endl;

	if ( !m_isJumping && !m_removingActivePart )
	{
		if ( KParts::ReadOnlyPart * ro_part = dynamic_cast<KParts::ReadOnlyPart*>(m_currentActivePart) )
		{
			addHistoryEntry( ro_part );
		}
	}

	m_currentActivePart = part;
	m_removingActivePart = false;

    if (part) {
        KXMLGUIClient* client = dynamic_cast<KXMLGUIClient*>(part->widget());
        if (client) Core::setupShourtcutTips(client);
    }

    updateMenuItems();
    TQTimer::singleShot( 100, this, TQT_SLOT(slotWaitForFactoryHack()) );
}

void PartController::showPart( KParts::Part* part, const TQString& name, const TQString& shortDescription )
{
  if (!part->widget()) {
    /// @todo error handling
    return; // to avoid later crash
  }

  TQPtrListIterator<KParts::Part> it(*parts());
  for ( ; it.current(); ++it)
  {
    if( it.current() == part ){
        // part already embedded
	activatePart( it.current() );
	return;
    }
  }

  // embed the part
  TopLevel::getInstance()->embedPartView( part->widget(), name, shortDescription );
  addPart( part );
}

void PartController::slotDocumentDirty( Kate::Document * d, bool isModified, unsigned char reason )
{
	kdDebug(9000) << k_funcinfo << endl;

	//	KTextEditor::Document * doc = reinterpret_cast<KTextEditor::Document*>( d ); // theoretically unsafe in MI scenario
	KTextEditor::Document * doc = 0;

	TQPtrListIterator<KParts::Part> it( *parts() );
	while( it.current() )
	{
		if ( (void*)it.current() == (void*)d )
		{
			doc = dynamic_cast<KTextEditor::Document*>( it.current() );
			break;
		}
		++it;
	}

	// this is a bit strange, but in order to avoid weird crashes
	// down in KDirWatcher, we want to step off the call chain before
	// opening any messageboxes
	if ( doc )
	{
		ModificationData * p = new ModificationData;
		p->doc = doc;
		p->isModified = isModified;
		p->reason = reason;
		KDevJobTimer::singleShot( 0, this, TQT_SLOT(slotDocumentDirtyStepTwo(void*)), p );
	}
}

void PartController::slotDocumentDirtyStepTwo( void * payload )
{
	if ( !payload ) return;

	ModificationData * p = reinterpret_cast<ModificationData*>( payload );
	KTextEditor::Document * doc = p->doc;

	// let's make sure the document is still loaded
	bool haveDocument = false;
	if( const TQPtrList<KParts::Part> * partlist = parts() )
	{
		TQPtrListIterator<KParts::Part> it( *partlist );
		while ( KParts::Part * part = it.current() )
		{
			if ( p->doc == dynamic_cast<KTextEditor::Document*>( part ) )
			{
				haveDocument = true;
				break;
			}
			++it;
		}
	}
	if ( !haveDocument ) return;

	bool isModified = p->isModified;
	unsigned char reason = p->reason;
	delete p;

	KURL url = storedURLForPart( doc );
	if ( url.isEmpty() )
	{
		kdDebug(9000) << "Warning!! the stored url is empty. Bailing out!" << endl;
	}

	if ( reason > 0 )
	{
		if ( !_dirtyDocuments.contains( doc ) )
		{
			_dirtyDocuments.append( doc );
		}

		if ( reactToDirty( url, reason ) )
		{
			// file has been reloaded
			emit documentChangedState( url, Clean );
			_dirtyDocuments.remove( doc );
		}
		else
		{
			doEmitState( url );
		}
	}
	else
	{
		_dirtyDocuments.remove( doc );
		emit documentChangedState( url, Clean );
	}

	kdDebug(9000) << doc->url().url() << endl;
	kdDebug(9000) << isModified << endl;
	kdDebug(9000) << reason << endl;
}

bool PartController::isDirty( KURL const & url )
{
	return _dirtyDocuments.contains( static_cast<KTextEditor::Document*>( partForURL( url ) ) );
}

bool PartController::reactToDirty( KURL const & url, unsigned char reason )
{
    TDEConfig *config = kapp->config();
    config->setGroup("Editor");
	TQString dirtyAction = config->readEntry( "DirtyAction" );

	if ( dirtyAction == "nothing" ) return false;

	bool isModified = true;
	if( KParts::ReadWritePart * part = dynamic_cast<KParts::ReadWritePart*>( partForURL( url ) ) )
	{
		isModified = part->isModified();
	}
	else
	{
		kdDebug(9000) << k_funcinfo << " Warning. Not a ReadWritePart." << endl;
		return false;
	}

	if ( isModified )
	{
		KMessageBox::sorry( TopLevel::getInstance()->main(),
			i18n("Conflict: The file \"%1\" has changed on disk while being modified in memory.\n\n"
					"You should investigate before saving to make sure you are not losing data.").arg( url.path() ),
			i18n("Conflict") );
		return false;
	}

	if ( reason == 3 ) // means the file was deleted
	{
		KMessageBox::sorry( TopLevel::getInstance()->main(),
			i18n("Warning: The file \"%1\" has been deleted on disk.\n\n"
					"If this was not your intention, make sure to save this file now.").arg( url.path() ),
			i18n("File Deleted") );
		return false;
	}

	if ( dirtyAction == "alert" )
	{
		if ( KMessageBox::warningYesNo( TopLevel::getInstance()->main(),
	   		i18n("The file \"%1\" has changed on disk.\n\nDo you want to reload it?").arg( url.path() ),
			i18n("File Changed"), i18n("Reload"), i18n("Do Not Reload") ) == KMessageBox::No )
		{
			return false;
		}
	}

	// here we either answered yes above or are in autoreload mode
	reloadFile( url );

	return true;
}

void PartController::slotNewDesignerStatus(const TQString &formName, int status)
{
	kdDebug(9000) << k_funcinfo << endl;
	kdDebug(9000) << " formName: " << formName << ", status: " << status << endl;
	emit documentChangedState( KURL::fromPathOrURL(formName), DocumentState(status) );
}

void PartController::slotNewStatus( )
{
	kdDebug(9000) << k_funcinfo << endl;

	TQObject * senderobj = TQT_TQOBJECT(const_cast<TQT_BASE_OBJECT_NAME*>( sender() ));
	KTextEditor::View * view = dynamic_cast<KTextEditor::View*>( senderobj );
	if ( view )
	{
		doEmitState( view->document()->url() );
	}
}

DocumentState PartController::documentState( KURL const & url )
{
	KParts::ReadWritePart * rw_part = dynamic_cast<KParts::ReadWritePart*>( partForURL( url ) );
	if ( !rw_part ) return Clean;

	DocumentState state = Clean;
	if ( rw_part->isModified() )
	{
		state = Modified;
	}

	if ( isDirty( url ) )
	{
		if ( state == Modified )
		{
			state = DirtyAndModified;
		}
		else
		{
			state = Dirty;
		}
	}

	return state;
}

void PartController::doEmitState( KURL const & url )
{
	emit documentChangedState( url, documentState( url ) );
}

KURL::List PartController::openURLs( )
{
	KURL::List list;
	TQPtrListIterator<KParts::Part> it(*parts());
	for ( ; it.current(); ++it)
	{
		if ( KParts::ReadOnlyPart *ro_part = dynamic_cast<KParts::ReadOnlyPart*>(it.current()) )
		{
			list << ro_part->url();
		}
	}
	return list;
}

/////////////////////////////////////////////////////////////////////////////

//BEGIN History methods

PartController::HistoryEntry::HistoryEntry( const KURL & u, int l, int c)
	: url(u), line(l), col(c)
{
	id = abs( TQTime::currentTime().msecsTo( TQTime() ) );	// should provide a reasonably unique number
}

void PartController::slotBack()
{
	HistoryEntry thatEntry = m_backHistory.front();
	m_backHistory.pop_front();
	m_backAction->setEnabled( !m_backHistory.isEmpty() );

	HistoryEntry thisEntry = createHistoryEntry();
	if ( !thisEntry.url.isEmpty() )
	{
		m_forwardHistory.push_front( thisEntry );
		m_forwardAction->setEnabled( true );
	}

	jumpTo( thatEntry );
}

void PartController::slotForward()
{
	HistoryEntry thatEntry = m_forwardHistory.front();
	m_forwardHistory.pop_front();
	m_forwardAction->setEnabled( !m_forwardHistory.isEmpty() );

	HistoryEntry thisEntry = createHistoryEntry();
	if ( !thisEntry.url.isEmpty() )
	{
		m_backHistory.push_front( thisEntry );
		m_backAction->setEnabled( true );
	}

	jumpTo( thatEntry );
}

void PartController::slotBackAboutToShow()
{
	TDEPopupMenu *popup = m_backAction->popupMenu();
	popup->clear();

	if ( m_backHistory.isEmpty()) return;

	int i = 0;
	TQValueList<HistoryEntry>::ConstIterator it = m_backHistory.begin();
	while( i < 10 && it != m_backHistory.end() )
	{
		popup->insertItem( (*it).url.fileName() + TQString(" (%1)").arg( (*it).line +1), (*it).id );
		++i;
		++it;
	}
}

void PartController::slotForwardAboutToShow( )
{
	TDEPopupMenu * popup = m_forwardAction->popupMenu();
	popup->clear();

	if ( m_forwardHistory.isEmpty() ) return;

	int i = 0;
	TQValueList<HistoryEntry>::ConstIterator it = m_forwardHistory.begin();
	while( i < 10 && it != m_forwardHistory.end() )
	{
		popup->insertItem( (*it).url.fileName() + TQString(" (%1)").arg( (*it).line +1), (*it).id );
		++i;
		++it;
	}
}

void PartController::slotBackPopupActivated( int id )
{
	TQValueList<HistoryEntry>::Iterator it = m_backHistory.begin();
	while( it != m_backHistory.end() )
	{
		if ( (*it).id == id )
		{
			HistoryEntry entry = *it;
			m_backHistory.erase( m_backHistory.begin(), ++it );
			m_backAction->setEnabled( !m_backHistory.isEmpty() );

			HistoryEntry thisEntry = createHistoryEntry();
			if ( !thisEntry.url.isEmpty() )
			{
				m_forwardHistory.push_front( thisEntry );
				m_forwardAction->setEnabled( true );
			}

			jumpTo( entry );
			return;
		}
		++it;
	}
}

void PartController::slotForwardPopupActivated( int id )
{
	TQValueList<HistoryEntry>::Iterator it = m_forwardHistory.begin();
	while( it != m_forwardHistory.end() )
	{
		if ( (*it).id == id )
		{
			HistoryEntry entry = *it;
			m_forwardHistory.erase( m_forwardHistory.begin(), ++it );
			m_forwardAction->setEnabled( !m_forwardHistory.isEmpty() );

			HistoryEntry thisEntry = createHistoryEntry();
			if ( !thisEntry.url.isEmpty() )
			{
				m_backHistory.push_front( thisEntry );
				m_backAction->setEnabled( true );
			}

			jumpTo( entry );
			return;
		}
		++it;
	}
}

void PartController::jumpTo( const HistoryEntry & entry )
{
	m_isJumping = true;
	editDocument( entry.url, entry.line, entry.col );
	m_isJumping = false;
}


PartController::HistoryEntry PartController::createHistoryEntry( KParts::ReadOnlyPart * ro_part ) {
    if( ro_part == 0 )
        ro_part = dynamic_cast<KParts::ReadOnlyPart*>( activePart() );

    if ( !ro_part ) return HistoryEntry();
    KTextEditor::ViewCursorInterface * cursorIface = dynamic_cast<KTextEditor::ViewCursorInterface*>( ro_part->widget() );
    if ( !cursorIface ) return HistoryEntry();

    uint line = 0;
    uint col = 0;
    cursorIface->cursorPositionReal( &line, &col );

    return HistoryEntry( ro_part->url(), line, col );
}


// this should be called _before_ a jump is made
void PartController::addHistoryEntry( KParts::ReadOnlyPart * part )
{
	if ( m_isJumping ) return;

	HistoryEntry thisEntry = createHistoryEntry( part );
	if ( !thisEntry.url.isEmpty() )
	{
		HistoryEntry lastEntry = m_backHistory.front();
		if ( (lastEntry.url.path() != thisEntry.url.path()) || (lastEntry.line != thisEntry.line) )
		{
			m_backHistory.push_front( thisEntry );
			m_backAction->setEnabled( true );

			m_forwardHistory.clear();
			m_forwardAction->setEnabled( false );
		}
		else
		{
			kdDebug(9000) << "** avoiding to create duplicate history entry **" << endl;
		}
	}
}

//END History methods

void PartController::slotWaitForFactoryHack( )
{
	//kdDebug(9000) << k_funcinfo << endl;

	if ( !activePart() ) return;

	if ( dynamic_cast<KTextEditor::View*>( activePart()->widget() ) )
	{
		if ( !activePart()->factory() )
		{
			TQTimer::singleShot( 100, this, TQT_SLOT(slotWaitForFactoryHack()) );
    		return;
		}
		else
		{
			EditorProxy::getInstance()->installPopup( activePart() );
		}
	}

    if ( MultiBuffer *multiBuffer =
         dynamic_cast<MultiBuffer*>(
            EditorProxy::getInstance()->topWidgetForPart( activePart() ) )
       )
    {
        KURL url = dynamic_cast<KParts::ReadOnlyPart*>( activePart() )->url();
        multiBuffer->activePartChanged( url );
        // Really unfortunate, but the mainWindow relies upon this
        // to set the tab's icon
        emit documentChangedState( url, documentState( url ) );
    }
}

KParts::ReadOnlyPart *PartController::qtDesignerPart()
{
	TQPtrListIterator<KParts::Part> it(*parts());
	for ( ; it.current(); ++it)
	{
		KInterfaceDesigner::Designer *des =  dynamic_cast<KInterfaceDesigner::Designer*>(it.current());
		if (des && des->designerType() == KInterfaceDesigner::TQtDesigner)
			return des;
	}
	return 0;
}

KTextEditor::Editor *PartController::openTextDocument( bool activate )
{
    KTextEditor::Editor *editorpart =
        createEditorPart( activate, false, KURL( i18n("unnamed") ) );

    if ( editorpart )
    {
        if ( !m_presetEncoding.isNull() )
        {
            KParts::BrowserExtension * extension =
                KParts::BrowserExtension::childObject( editorpart );
            if ( extension )
            {
                KParts::URLArgs args;
                args.serviceType = TQString( "text/plain;" ) + m_presetEncoding;
                extension->setURLArgs(args);
            }
            m_presetEncoding = TQString();
        }

        TQWidget * widget =
            EditorProxy::getInstance()->topWidgetForPart( editorpart );

        addHistoryEntry();
        integratePart(editorpart, KURL(i18n("unnamed")), widget, true, true);

        EditorProxy::getInstance()->setLineNumber(editorpart, 0, 0);
    }

    return editorpart;
}

void PartController::textChanged()
{
	if ( KTextEditor::Document * doc = dynamic_cast<KTextEditor::Document*>( activePart() ) )
	{
		if ( KTextEditor::ViewCursorInterface * vci = dynamic_cast<KTextEditor::ViewCursorInterface*>( doc->widget() ) )
		{
			m_gotoLastEditPosAction->setEnabled( true );

			m_lastEditPos.url = doc->url();
			vci->cursorPosition( &m_lastEditPos.pos.first, &m_lastEditPos.pos.second );
		}
	}
}

void PartController::gotoLastEditPos()
{
	editDocument( m_lastEditPos.url, m_lastEditPos.pos.first, m_lastEditPos.pos.second );
}

void PartController::slotDocumentUrlChanged()
{
    TQObject *obj = TQT_TQOBJECT(const_cast<TQT_BASE_OBJECT_NAME*>(sender()));
    KTextEditor::Document *doc = dynamic_cast<KTextEditor::Document*>( obj );
    if (!doc)
        return;

    MultiBuffer *multiBuffer = dynamic_cast<MultiBuffer*>(
        EditorProxy::getInstance()->findPartWidget( doc ));
    if (!multiBuffer)
        return;

    multiBuffer->updateUrlForPart(doc, doc->url());
    updatePartURL(doc);
    emit partURLChanged(doc);
}

#include "partcontroller.moc"

