/***************************************************************************
 *
 * Copyright (C) 2005 Elad Lahav (elad_lahav@users.sourceforge.net)
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 ***************************************************************************/

#include <qfile.h>
#include <kfiledialog.h>
#include <kmenubar.h>
#include <klocale.h>
#include <kmessagebox.h>
#include <klineedit.h>
#include <kinputdialog.h>
#include <kxmlguifactory.h>
#include <kstatusbar.h>
#include <kurldrag.h>
#include <kkeydialog.h>
#include "kscope.h"
#include "kscopeconfig.h"
#include "projectmanager.h"
#include "editortabs.h"
#include "fileview.h"
#include "filelist.h"
#include "querywidget.h"
#include "editormanager.h"
#include "cscopefrontend.h"
#include "ctagslist.h"
#include "newprojectdlg.h"
#include "openprojectdlg.h"
#include "preferencesdlg.h"
#include "dirscanner.h"
#include "querypage.h"
#include "calltreedlg.h"
#include "calltreemanager.h"
#include "kscopepixmaps.h"
#include "progressdlg.h"
#include "projectfilesdlg.h"
#include "cscopemsgdlg.h"
#include "symboldlg.h"
#include "symbolcompletion.h"
#include "queryviewdlg.h"
#include "graphwidget.h"
#include "makedlg.h"
#include "welcomedlg.h"
#include "bookmarksdlg.h"
#include "kscopeactions.h"

/**
 * Class constructor.
 * @param	pParent	The parent widget
 * @param	szName	The widget's name
 */
KScope::KScope(QWidget* pParent, const char* szName) :
	KParts::DockMainWindow(pParent, szName),
	m_pCscopeBuild(NULL),	
	m_sCurFilePath(""),
	m_nCurLine(0),
	m_pProgressDlg(NULL),
	m_bUpdateGUI(true),
	m_bCscopeVerified(false),
	m_bRebuildDB(false),
	m_pMakeDlg(NULL)
{
	QString sPath;

	// Load configuration
	Config().load();
	
	// Create the main child widgets
	m_pEditTabs = new EditorTabs(this, NULL);
	m_pQueryWidget = new QueryWidget(this);
	m_pFileView = new FileView(this);
	m_pFileList = m_pFileView->getFileList();
	m_pMsgDlg = new CscopeMsgDlg(this);
	m_pQueryDock = createDockWidget("Query Window", QPixmap());
	m_pFileViewDock = createDockWidget("File List Window", QPixmap());

	// Connect menu and toolbar items with the object's slots
	m_pActions = new KScopeActions(this);
	m_pActions->init();
	m_pActions->slotEnableProjectActions(false);
	
	// Show a toolbar show/hide menu
	setStandardToolBarMenuEnabled(true);
	
	// Create the initial GUI (no active part)
	setXMLFile("kscopeui.rc");
	createShellGUI();
	
	// Create all child widgets
	initMainWindow();

	// Create control objects
	m_pProjMgr = new ProjectManager();
	m_pEditMgr = new EditorManager(this);
	m_pCallTreeMgr = new CallTreeManager(this);

	// Initialise the icon manager	
	Pixmaps().init();
	
	// Open a file for editing when selected in the project's file list or the
	// file tree
	connect(m_pFileView, SIGNAL(fileRequested(const QString&, uint)), this,
		SLOT(slotShowEditor(const QString&, uint)));

	// Delete an editor page object after it is removed
	connect(m_pEditTabs, SIGNAL(editorRemoved(EditorPage*)),
		this, SLOT(slotDeleteEditor(EditorPage*)));
	
	connect(m_pEditTabs, SIGNAL(filesDropped(QDropEvent*)), this,
		SLOT(slotDropEvent(QDropEvent*)));
	
	// Set an editor as the active part whenever its owner tab is selected
	connect(m_pEditTabs, SIGNAL(editorChanged(EditorPage*, EditorPage*)),
		this, SLOT(slotChangeEditor(EditorPage*, EditorPage*)));

	// Display a file at a specific line when selected in a query list
	connect(m_pQueryWidget, SIGNAL(lineRequested(const QString&, uint)),
		this, SLOT(slotQueryShowEditor(const QString&, uint)));
	
	// Display the symbol dialogue when the user opens a new query page
	connect(m_pQueryWidget, SIGNAL(newQuery()), 
		this, SLOT(slotQueryReference()));

	// Rebuild the project database after a certain time period has elapsed
	// since the last save
	connect(&m_timerRebuild, SIGNAL(timeout()), this, SLOT(slotRebuildDB()));

	// Display a file at a specific line when selected in a call tree dialogue
	connect(m_pCallTreeMgr, SIGNAL(lineRequested(const QString&, uint)),
		this, SLOT(slotQueryShowEditor(const QString&, uint)));
		
	// Store main window settings when closed
	setAutoSaveSettings();
	
	// Initialise arrow head drawing
	GraphWidget::setArrowInfo(20, 15);
	
	// Use a maximised window the first time
	if (Config().isFirstTime())
		showMaximized();

	// Show the Welcome message
	if (Config().showWelcomeDlg()) {
		show();
		slotShowWelcome();
	}

	// If this is the first time the user has launched KScope, prompt him/her
	// to configure the global parameters
	if (Config().isFirstTime())
		slotConfigure();		
}

/**
 * Class destructor.
 */
KScope::~KScope()
{
	// Save configuration
	Config().store();
	Config().storeWorkspace(this);
	
	delete m_pCallTreeMgr;
	delete m_pEditMgr;
	delete m_pCscopeBuild;
	delete m_pProjMgr;
	
	if (m_pMakeDlg != NULL)
		delete m_pMakeDlg;
}

/**
 * Positions child widgets into their docking stations, and performs some
 * other main window initialisation.
 */
void KScope::initMainWindow()
{
	KStatusBar* pStatus;
	KDockWidget* pMainDock;
	QPopupMenu* pPopup;
	
	// Create the status bar
	pStatus = statusBar();
	pStatus->insertItem(i18n(" Line: N/A Col: N/A "), 0, 0, true);

	// Create the main dock for the editor tabs widget
	pMainDock = createDockWidget("Editors Window", QPixmap());
	pMainDock->setWidget(m_pEditTabs);
	pMainDock->setDockSite(KDockWidget::DockCorner);
	setMainDockWidget(pMainDock);
	setView(pMainDock);
	pMainDock->setEnableDocking(KDockWidget::DockNone);

	// Create the query window dock
	m_pQueryDock->setWidget(m_pQueryWidget);
	m_pQueryDock->manualDock(pMainDock, KDockWidget::DockBottom, 65);

	// Update the relevant shell action when the dock is hidden through its
	// close button
	connect(m_pQueryDock, SIGNAL(headerCloseButtonClicked()), m_pActions,
		SLOT(slotQueryDockClosed()));
		
	// Create the file view dock
	m_pFileViewDock->setWidget(m_pFileView);
	m_pFileViewDock->manualDock(pMainDock, KDockWidget::DockRight, 80);
	
	// Update the relevant shell action when the dock is hidden through its
	// close button
	connect(m_pFileViewDock, SIGNAL(headerCloseButtonClicked()), m_pActions,
		SLOT(slotFileViewDockClosed()));
	
	// Associate the "Window" menu with the editor tabs widdget
	pPopup = (QPopupMenu*)factory()->container("window", this);
	m_pEditTabs->setWindowMenu(pPopup);

	// Associate the "Query" popup menu with the query widget
	pPopup = (QPopupMenu*)factory()->container("query_popup", this);
	m_pQueryWidget->setPageMenu(pPopup, m_pActions->getLockAction());
	
	// Restore dock configuration
	Config().loadWorkspace(this);
	m_bHideQueryOnSelection = m_pQueryDock->isHidden();
	m_pActions->initLayoutActions();
}

/**
 * Handles the "File->Quit" command. Closes the main window, which terminates
 * the application.
 */
void KScope::slotClose()
{
	// Destroy the main window
	KParts::DockMainWindow::close();
}

/**
 * Called when a request has been issued to close the main window.
 * Tries to close the active project.
 * @return	true if the main window can be closed, false otherwise
 */
bool KScope::queryClose()
{
	bool bResult;

	m_bUpdateGUI = false;
	bResult = slotCloseProject();
	m_bUpdateGUI = true;

	return bResult;
}

/**
 * Handles the "Project->New..." command.
 * Prompts the user for the name and folder for the project, and then creates
 * the project.
 */
void KScope::slotCreateProject()
{
	NewProjectDlg dlg(true, this);
	ProjectBase::Options opt;
	QString sProjPath;
	
	// Prompt the user to close any active projects
	if (m_pProjMgr->curProject()) {
		if (KMessageBox::questionYesNo(0, 
			i18n("The current project needs to be closed before a new one is"
			" created.\nWould you like to close it now?")) != 
			KMessageBox::Yes) {
			return;
		}
		
		// Try to close the project.
		if (!slotCloseProject())
			return;
	}
	
	// Display the "New Project" dialog
	if (dlg.exec() != QDialog::Accepted)
		return;

	// Create and open the new project
	dlg.getOptions(opt);
	if (m_pProjMgr->create(dlg.getName(), dlg.getPath(), opt, sProjPath))
		openProject(sProjPath);
}

/**
 * Handles the "Project->Open..." command.
 * Prompts the user for a project file ("cscope.proj"), and opens the
 * selected project.
 */
void KScope::slotOpenProject()
{
	OpenProjectDlg dlg;
	QString sPath;
	
	if (dlg.exec() == QDialog::Rejected)
		return;

	sPath = dlg.getPath();
	
	// Check if the path refers to a permanent or temporary project
	if (QFileInfo(sPath).isDir())
		openProject(sPath);
	else
		openCscopeOut(sPath);
}

/**
 * Handles the "Project->Add/Remove Files..." command.
 * Opens the project's files dialog, which allows the user to add and remove
 * source files.
 */
void KScope::slotProjectFiles()
{
	ProjectBase* pProj;
	
	// A project must be open
	pProj = m_pProjMgr->curProject();
	if (!pProj)
		return;

	// Cannot update the file list of a temporary project
	if (pProj->isTemporary()) {
		KMessageBox::error(0, i18n("The Add/Remove Files dialogue is not "
				"available for temporary projects."));
		return;
	}

	// Display the files dialog
	ProjectFilesDlg dlg((Project*)pProj, this);
	if (dlg.exec() != QDialog::Accepted)
		return;
	
	// Update the project's file list
	if (pProj->storeFileList(&dlg))
		slotProjectFilesChanged();
}

/**
 * Handles the "Project->Properties..." command.
 * Opens the project's properties dialog, which allows the user to change
 * some attributes of the current project.
 * source files.
 */
void KScope::slotProjectProps()
{
	ProjectBase* pProj;
	ProjectBase::Options opt;
	
	// A project must be open
	pProj = m_pProjMgr->curProject();
	if (!pProj)
		return;

	// No properties for a temporary project
	if (pProj->isTemporary()) {
		KMessageBox::error(0, i18n("The Project Properties dialogue is not "
			"available for temporary projects."));
		return;
	}
	
	// Create the properties dialog
	NewProjectDlg dlg(false, this);
	pProj->getOptions(opt);
	dlg.setProperties(pProj->getName(), pProj->getPath(), opt);
		
	// Display the properties dialog
	if (dlg.exec() != QDialog::Accepted)
		return;

	// Set new properties
	dlg.getOptions(opt);
	pProj->setOptions(opt);
	
	// Reset the CscopeFrontend class and the builder object
	initCscope();
	
	// Set auto-completion parameters
	SymbolCompletion::initAutoCompletion(opt.bACEnabled, opt.nACMinChars,
		opt.nACDelay, opt.nACMaxEntries);
	
	// Set per-project command-line arguments for Ctags
	CtagsFrontend::setExtraArgs(opt.sCtagsCmd);
	
	// Set the source root
	m_pFileView->setRoot(pProj->getSourceRoot());
}

/**
 * Handles the "Cscope->Open Cscope.out..." menu command.
 * Prompts the user for a Cscope.out file, and, if successful, opens a new
 * session for working with this file.
 */
void KScope::slotProjectCscopeOut()
{
	QString sFilePath;
	
	// Prompt for a Cscope.out file
	sFilePath = KFileDialog::getOpenFileName();
	if (sFilePath.isEmpty())
		return;

	// Open a temporary project
	openCscopeOut(sFilePath);	
}

/**
 * Handles the "Cscope->References..." menu command.
 * Prompts the user for a symbol name, and initiates a query to find all 
 * references to that symbol.
 */
void KScope::slotQueryReference()
{
	slotQuery(SymbolDlg::Reference, true);
}

/**
 * Handles the "Cscope->Definition..." menu command.
 * Prompts the user for a symbol name, and initiates a query to find the 
 * global definition of that symbol.
 */
void KScope::slotQueryDefinition()
{
	slotQuery(SymbolDlg::Definition, true);
}

/**
 * Handles the "Cscope->Called Functions..." menu command.
 * Prompts the user for a function name, and initiates a query to find all 
 * function calls from that function.
 */
void KScope::slotQueryCalled()
{
	slotQuery(SymbolDlg::Called, true);
}

/**
 * Handles the "Cscope->Calling Functions..." menu command.
 * Prompts the user for a function name, and initiates a query to find all 
 * functions calling that function.
 */
void KScope::slotQueryCalling()
{
	slotQuery(SymbolDlg::Calling, true);
}

/**
 * Handles the "Cscope->Find Text..." menu command.
 * Prompts the user for a string, and initiates a query to find all
occurances 
 * of that string.
 */
void KScope::slotQueryText()
{
	slotQuery(SymbolDlg::Text, true);
}

/**
 * Handles the "Cscope->Find EGrep Pattern..." menu command.
 * Prompts the user for a regular expression, and initiates a query to find 
 * all strings matching that pattern.
 */
void KScope::slotQueryPattern()
{
	slotQuery(SymbolDlg::Pattern, true);
}

/**
 * Handles the "Cscope->Find File..." menu command.
 * Prompts the user for a file name, and initiates a query to find all files
 * having that name.
 */
void KScope::slotQueryFile()
{
	slotQuery(SymbolDlg::FileName, true);
}

/**
 * Handles the "Cscope->Find Including Files..." menu command.
 * Prompts the user for a file name, and initiates a query to find all files
 * having an '#include' directive to that file.
 */
void KScope::slotQueryIncluding()
{
	slotQuery(SymbolDlg::Including, true);
}

/**
 * Handles the "Cscope->Quick Definition" menu command.
 * Initiates a query to find the global definition of the symbol currently
 * selected or under the cursor. The user is prompted only if no symbol can
 * be found.
 */
void KScope::slotQueryQuickDef()
{
	QString sSymbol;
	QueryViewDlg* pDlg;
	uint nType;
	bool bCase;
	
	// Get the requested symbol and query type
	nType = SymbolDlg::Definition;
	if (!getSymbol(nType, sSymbol, bCase, false))
		return;
		
	// Create a modeless query view dialogue
	pDlg = new QueryViewDlg(QueryViewDlg::DestroyOnSelect, this);
	
	// Display a line when it is selected in the dialogue
	connect(pDlg, SIGNAL(lineRequested(const QString&, uint)), this,
		SLOT(slotShowEditor(const QString&, uint)));
		
	// Start the query
	pDlg->query(nType, sSymbol);
}

/**
 * Handles the "Cscope->Call Tree..." menu command.
 * Displays a tree of functions calling the requested function.
 */
void KScope::slotCallTree()
{
	slotQuery(SymbolDlg::CallTree, true);
}

/**
 * Handles the "Cscope->Rebuild Database..." command.
 * Rebuilds Cscope's database for the current project.
 */
void KScope::slotRebuildDB()
{
	ProjectBase* pProj;
	
	pProj = m_pProjMgr->curProject();
	if (!pProj)
		return;
	
	if (!pProj->dbExists()) {
		m_pProgressDlg = new ProgressDlg(i18n("KScope"), i18n("Please wait "
			"while KScope builds the database"), this);
		m_pProgressDlg->setAllowCancel(false);
		m_pProgressDlg->setValue(0);
	}
	
	m_pCscopeBuild->rebuild();
}

/**
 * Handles the "Settings->Configure Shortcuts..." command.
 * Displays the prferences dialog, which allows the user 
 * to change the shortcuts for KScope.
 */
void KScope::slotShortcuts()
{
	KKeyDialog::configure(actionCollection(), this);
}

/**
 * Handles the "Settings->Configure KScope..." command.
 * Displays the prferences dialog, which allows the user to set different
 * configuration parameters for KScope.
 */
void KScope::slotConfigure()
{
	PreferencesDlg dlg;

	// Apply the preferences if either the "Apply" or the "OK" buttons are
	// clicked
	connect(&dlg, SIGNAL(applyPref()), this, SLOT(slotApplyPref()));

	// Show the dialog
	if (dlg.exec() == QDialog::Accepted) {
		// Verify Cscope's installation
		verifyCscope();
	}
}

/**
 * Refreshes the file list when files are added to or removed from a project,
 * and rebuilds the Cscope database.
 * This slot is attached to the fileListChanged() signal emitted by 
 * the ProjectManager object.
 */
void KScope::slotProjectFilesChanged()
{
	QStringList slArgs;
	
	// Refresh the file list
	m_pFileList->setUpdatesEnabled(false);
	m_pFileList->clear();
	m_pProjMgr->curProject()->loadFileList(m_pFileList);
	m_pFileList->setUpdatesEnabled(true);
	
	// Rebuild the symbol database
	if (isAutoRebuildEnabled())
		slotRebuildDB();
}

/**
 * Adds a list of files to the current project.
 * This slot is connected to the filesAdded() signal of the ProjectManager
 * object. Once files are added to the project, they are also added to the
 * file list, and the project's database is rebuilt.
 * @param	slFiles	The list of file paths added to the project
 */
void KScope::slotFilesAdded(const QStringList& slFiles)
{
	QStringList::const_iterator itr;

	// Add the file paths to the project's file list
	for (itr = slFiles.begin(); itr != slFiles.end(); ++itr)
		m_pFileList->addItem(*itr);
	
	// Rebuild the database
	if (isAutoRebuildEnabled())
		slotRebuildDB();
}

/**
 * Promts the user for a symbol, an starts a new Cscope query.
 * @param	nType	The numeric query type code
 * @param	bPrompt	true to always prompt for a symbol, false to try to
 * 					obtain the symbol automatically
 */
void KScope::slotQuery(uint nType, bool bPrompt)
{
	QString sSymbol;
	CallTreeDlg* pCallTreeDlg;
	bool bCase;
	
	// Get the requested symbol and query type
	if (!getSymbol(nType, sSymbol, bCase, bPrompt))
		return;
		
	if (nType == SymbolDlg::CallTree) {
		// Create and display a call tree dialogue
		pCallTreeDlg = m_pCallTreeMgr->addDialog();
		pCallTreeDlg->setRoot(sSymbol);
		pCallTreeDlg->show();
	}
	else {
		// Run the requested query
		nType = SymbolDlg::getQueryType(nType);
		m_pQueryWidget->initQuery(nType, sSymbol, bCase);
		
		// Ensure Query Window is visible
		toggleQueryWindow(true);	
	}
}

/**
 * Opens a project.
 * If another project is currently active, it is closed first.
 * @param	sDir	The directory of the project to open.
 */
void KScope::openProject(const QString& sDir)
{
	QString sProjDir;
	ProjectBase* pProj;
	QStringList slQueryFiles;
	QStringList slCallTreeFiles;
	QStringList slArgs;
	ProjectBase::Options opt;
	
	// Close the current project (may return false if the user clicks on the
	// "Cancel" button while prompted to save a file)
	if (!slotCloseProject())
		return;

	// Open the project in the project manager
	sProjDir = QDir::cleanDirPath(sDir);
	if (!m_pProjMgr->open(sProjDir))
		return;
	
	// Change main window title
	pProj = m_pProjMgr->curProject();
	setCaption(pProj->getName());

	// Set the root of the file tree
	m_pFileView->setRoot(pProj->getSourceRoot());
	
	// Initialise Cscope and create a builder object
	initCscope();
	
	// Set auto-completion parameters
	pProj->getOptions(opt);
	SymbolCompletion::initAutoCompletion(opt.bACEnabled, opt.nACMinChars,
		opt.nACDelay, opt.nACMaxEntries);
	
	// Set per-project command-line arguments for Ctags
	CtagsFrontend::setExtraArgs(opt.sCtagsCmd);
	
	// Create an initial query page
	m_pQueryWidget->addQueryPage();
	
	// Enable project-related actions
	m_pActions->slotEnableProjectActions(true);
	
	// If this is a new project (i.e., no source files are yet included), 
	// display the project files dialogue
	if (pProj->isEmpty()) {
		slotProjectFiles();
		return;
	}
	
	// Fill the file list with all files in the project. 
	m_pFileList->setUpdatesEnabled(false);
	pProj->loadFileList(m_pFileList);
	m_pFileList->setUpdatesEnabled(true);
	
	// Restore the last session
	restoreSession();
	
	// Rebuild the cross-reference database
	if (isAutoRebuildEnabled()) {
		// If Cscope installation was not yet verified, postpone the build
		// process
		if (m_bCscopeVerified)
		slotRebuildDB();
		else
			m_bRebuildDB = true;
	}
}

/**
 * Opens a temporary project for a Cscope.out file.
 * @param	sFilePath	The full path of the Cscope.out file
 * @return	true if successful, false otherwise
 */
bool KScope::openCscopeOut(const QString& sFilePath)
{
	ProjectBase* pProj;
	
	// Close the current project (may return false if the user clicks on the
	// "Cancel" button while prompted to save a file)
	if (!slotCloseProject())
		return false;

	// Open a temporary project for this cscope.out file
	if (!m_pProjMgr->openCscopeOut(sFilePath))
		return false;
	
	// Change main window title
	pProj = m_pProjMgr->curProject();
	setCaption(pProj->getName());
	
	// Set the root folder in the file tree
	m_pFileView->setRoot(pProj->getSourceRoot());
	
	// Initialise Cscope and create a builder object
	initCscope();
	
	// Create an initial query page
	m_pQueryWidget->addQueryPage();
	
	// Enable project-related actions
	m_pActions->slotEnableProjectActions(true);
	
	// Fill the file list with all files in the project. 
	m_pFileList->setUpdatesEnabled(false);
	pProj->loadFileList(m_pFileList);
	m_pFileList->setUpdatesEnabled(true);
	
	return true;
}

/**
 * Opens the most recently used project.
 * This method is called when KScope starts, but only if the relevant 
 * configuration flag (Reload Last Project) is set.
 */
void KScope::openLastProject()
{
	const QStringList slProjects = Config().getRecentProjects();
	QString sPath;
	
	if (slProjects.empty())
		return;
		
	// Get the project's path
	sPath = *slProjects.begin();

	// Check if the path refers to a temporary project
	if (!QFileInfo(sPath).isDir()) {
		openCscopeOut(sPath);
		return;
	}

	openProject(sPath);
}

/**
 * Reopens all files which were open when the project was last closed.
 * In order to reduce the time required by this operation, the GUI of all
 * but the last editor part is not merged with that of the main window.
 */
void KScope::restoreSession()
{
	ProjectBase* pProj;
	Project::Session sess;
	FileLocation* pLoc;
	EditorPage* pPage;
	
	// A session is available for persistent projects only
	pProj = m_pProjMgr->curProject();
	if (!pProj || pProj->isTemporary())
		return;
	
	// Make sure all FileLocation objects are deleted
	sess.fllOpenFiles.setAutoDelete(true);
	sess.fllBookmarks.setAutoDelete(true);
	
	// Load the session
	((Project*)pProj)->loadSession(sess);
	
	// Do not update the GUI when loading the editor parts of the initially
	// hidden windows
	m_bUpdateGUI = false;
	
	for (pLoc = sess.fllOpenFiles.first(); pLoc != NULL;
		pLoc = sess.fllOpenFiles.next()) {
		if (QFile::exists(pLoc->m_sPath)) {
			pPage = addEditor(pLoc->m_sPath);
			pPage->setCursorPos(pLoc->m_nLine, pLoc->m_nCol);
		}
	}
	
	// Merge the GUI of the visible editor part
	m_bUpdateGUI = true;

	// Set the active editor (or choose a default one)
	if (m_pEditTabs->findEditorPage(sess.sLastFile, true) == NULL)
		m_pEditTabs->findEditorPage(sess.fllOpenFiles.last()->m_sPath, true);
	
	// Reload bookmarks
	m_pEditTabs->setBookmarks(sess.fllBookmarks);	
	
	// Load previously stored queries and call trees
	m_pQueryWidget->loadPages(pProj->getPath(), sess.slQueryFiles);
	m_pCallTreeMgr->loadOpenDialogs(pProj->getPath(), sess.slCallTreeFiles);
}

/**
 * Shows or hides the query dock window.
 * This function is only called internally, not as a result of a user's
 * workspace action (e.g., clicking the "Show/Hide Query Window" toolbar
 * button). Therefore it does not reflect the user's preference, which is
 * kept through the m_bHideQueryOnSelection variable.
 * @param	bShow	true to show the window, false to hide it
 */
void KScope::toggleQueryWindow(bool bShow)
{
	// Remember the user's preferences
	if (bShow)
		m_bHideQueryOnSelection = m_pQueryDock->isHidden();
	else
		m_bHideQueryOnSelection = false;
	
	// Change the visibility state of the widget, if required
	if (m_pQueryDock->isShown() != bShow)
		m_pQueryDock->changeHideShowState();
		
	// Synchronise with the menu command's state
	m_pActions->slotQueryDockToggled(bShow);
}

/**
 * Parses the command line, after it was stripped of its KDE options.
 * The command line may contain one of the following options:
 * 1. A project file (named cscope.proj)
 * 2. A Cscope cross-reference database
 * 3. A list of source files
 * @param	pArgs	Command line arguments
 */
void KScope::parseCmdLine(KCmdLineArgs* pArgs)
{
	QString sArg;
	QFileInfo fi;
	int i;

	// Loop over all arguments
	for (i = 0; i < pArgs->count(); i++) {
		// Verify the argument is a file or directory name
		sArg = pArgs->arg(i);
		fi.setFile(sArg);
		if (!fi.exists())
			continue;
			
		// Handle the current argument
		if (fi.isFile()) {
			if (fi.fileName() == "cscope.proj") {
				// Open a project file
				openProject(fi.dirPath(true));
				return;
			} else if (openCscopeOut(sArg)) {
				// Opened the file as a cross-reference database
				return;
			} else {
				// Assume this is a source file
				slotShowEditor(sArg, 0);
			}
		} else if (fi.isDir()) {
			// Treat the given path as a project directory
			openProject(fi.absFilePath());
			return;
		}
	}
}

/**
 * Starts a shell script to ensure that Cscope is properly installed and to
 * extract the supported command-line arguments. 
 */
void KScope::verifyCscope()
{
	CscopeVerifier* pVer;
	
	statusBar()->message(i18n("Verifying Cscope installation..."));
	
	pVer = new CscopeVerifier();
	connect(pVer, SIGNAL(done(bool, uint)), this,
		SLOT(slotCscopeVerified(bool, uint)));
	
	pVer->verify();
}

/**
 * Initialises the CscopeFrontend class with the current project arguments,
 * and creates an object used for rebuilding the symbol database.
 */
void KScope::initCscope()
{
	ProjectBase* pProj;
	
	// Delete the current object, if one exists
	if (m_pCscopeBuild)
		delete m_pCscopeBuild;

	// Initialise CscopeFrontend
	pProj = m_pProjMgr->curProject();
	CscopeFrontend::init(pProj->getPath(), pProj->getArgs());

	// Create a persistent Cscope process
	m_pCscopeBuild = new CscopeFrontend();

	// Show build progress information in the main status bar
	connect(m_pCscopeBuild, SIGNAL(progress(int, int)), this,
		SLOT(slotBuildProgress(int, int)));
	connect(m_pCscopeBuild, SIGNAL(buildInvIndex()), this,
		SLOT(slotBuildInvIndex()));
	connect(m_pCscopeBuild, SIGNAL(finished(uint)), this,
		SLOT(slotBuildFinished(uint)));
	connect(m_pCscopeBuild, SIGNAL(aborted()), this,
		SLOT(slotBuildAborted()));

	// Show errors in a modeless dialogue
	connect(m_pCscopeBuild, SIGNAL(error(const QString&)), this,
		SLOT(slotCscopeError(const QString&)));
}

/**
 * Closes the active project.
 * Closing a project involves closing all of the editor windows (prompting
 * the user for unsaved changes); terminating the Cscope process; and further
 * clean-up of the project's data.
 */
bool KScope::slotCloseProject()
{
	ProjectBase* pProj;
	Project::Session sess;
	
	// Do nothing if no project is open
	pProj = m_pProjMgr->curProject();
	if (!pProj)
		return true;
	
	// Make sure all FileLocation objects are deleted
	sess.fllOpenFiles.setAutoDelete(true);
	sess.fllBookmarks.setAutoDelete(true);
	
	// Close all open editor pages
	if (m_pEditTabs->count() > 0) {
		// Save session information for persistent projects
		if (!pProj->isTemporary()) {
			sess.sLastFile = m_pEditTabs->getCurrentPage()->getFilePath();
			m_pEditTabs->getOpenFiles(sess.fllOpenFiles);
			m_pEditTabs->getBookmarks(sess.fllBookmarks);
		}
		
		if (!m_pEditTabs->removeAllPages())
			return false;
	}
	
	// Disable project-related actions
	m_pActions->slotEnableProjectActions(false);
	
	// Destroy the make dialogue
	if (m_pMakeDlg != NULL) {
		// Save session information for persistent projects
		if (!pProj->isTemporary()) {
			sess.sMakeCmd = m_pMakeDlg->getCommand();
			sess.sMakeRoot = m_pMakeDlg->getDir();
		}
		
		delete m_pMakeDlg;
		m_pMakeDlg = NULL;
	}
	
	// Save session information for persistent projects
	if (!pProj->isTemporary()) {
		m_pQueryWidget->savePages(pProj->getPath(), sess.slQueryFiles);
		m_pCallTreeMgr->saveOpenDialogs(pProj->getPath(), sess.slCallTreeFiles);
	}
		
	// Close all query pages and call trees
	m_pQueryWidget->slotCloseAll();
	m_pCallTreeMgr->closeAll();
	
	// Store session information for persistent projects
	if (!pProj->isTemporary())
		((Project*)pProj)->storeSession(sess);
	
	// Close the project in the project manager, and terminate the Cscope
	// process
	m_pProjMgr->close();
	delete m_pCscopeBuild;
	m_pCscopeBuild = NULL;
	setCaption(QString::null);

	// Clear the contents of the file list
	m_pFileView->clear();

	// Reset queried symbols history
	SymbolDlg::resetHistory();
	
    // Remove any remaining status bar messages
    statusBar()->message("");
    
	return true;
}

/**
 * Handles the "Edit->Edit in External Editor" menu command.
 * Invokes an external editor for the current file and line number.
 */
void KScope::slotExtEdit()
{
	QString sCmdLine;
	KProcess proc;

	// Create the command line for the external editor	
	sCmdLine = Config().getExtEditor();
	sCmdLine.replace("%F", m_sCurFilePath);
	sCmdLine.replace("%L", QString::number(m_nCurLine));
	
	// Run the external editor
	proc.setUseShell(true);
	proc << sCmdLine;
	proc.start(KProcess::DontCare);
}

/**
 * Handles the "Edit->Complete Symbol" menu command.
 * Creates a list of possible completions for the symbol currently under the
 * cursor.
 */
void KScope::slotCompleteSymbol()
{
	EditorPage* pPage;
	
	pPage = m_pEditTabs->getCurrentPage();
	if (pPage != NULL)
		pPage->slotCompleteSymbol();
}

/**
 * Handles the "Help->Show Welcome Message..." menu command.
 * Displays the "Welcome" dialogue.
 */
void KScope::slotShowWelcome()
{
	WelcomeDlg dlg;
	dlg.exec();
}

/**
 * Handles the "Edit->Go To Tag" menu command.
 * Sets the cursor to the edit box of the current tag list.
 */
void KScope::slotGotoTag()
{
	EditorPage* pPage;
	
	pPage = m_pEditTabs->getCurrentPage();
	if (pPage)
		pPage->setTagListFocus();
}

/**
 * Reports the results of the Cscope verification script.
 * This slot is connected to the done() signal emitted by the CscopeVerifier
 * object constructed in verifyCscope().
 */
void KScope::slotCscopeVerified(bool bResult, uint nArgs)
{
	statusBar()->message(i18n("Verifying Cscope installation...Done"), 3000);
	
	// Mark the flag even if Cscope was not found, to avoid nagging the user
	// (who may wish to use KScope even with Cscope disabled)
	m_bCscopeVerified = true;

	// Prompt the user in case Cscope is not properly installed
	if (!bResult) {
		KMessageBox::error(0, i18n("Cscope may not be properly installed on "
			"this system.\nPlease check the Cscope path specified in KScope's "
			"configuration dialogue."));
		slotConfigure();
		return;
	}
		
	// Set the discoverred supported command-line arguments
	CscopeFrontend::setSupArgs(nArgs);
	
	// Build the database, if required
	if (m_bRebuildDB) {
		m_bRebuildDB = false;
		slotRebuildDB();
	}
}

/**
 * Handles the "Project->Make..." menu command.
 * Displays the make dialogue.
 */
void KScope::slotProjectMake()
{
	QString sCmd, sDir;
	
	// Create the make dialogue, if it does not exist
	if (m_pMakeDlg == NULL) {
		// Create the dialogue
		m_pMakeDlg = new MakeDlg();
		
		// Set make parameters for this project
		m_pProjMgr->curProject()->getMakeParams(sCmd, sDir);
		m_pMakeDlg->setCommand(sCmd);
		m_pMakeDlg->setDir(sDir);
		
		// Show the relevant source location when an error link is clicked
		connect(m_pMakeDlg, SIGNAL(fileRequested(const QString&, uint)), this,
			SLOT(slotShowEditor(const QString&, uint)));
		
		// Show the dialogue
		m_pMakeDlg->show();
	}
	else if (m_pMakeDlg->isShown()) {
		// The dialogue exists, and is visible, just raise it
		m_pMakeDlg->raise();
		m_pMakeDlg->setActiveWindow();
	}
	else {
		// The dialogue exists but is closed, show it
		m_pMakeDlg->show();
	}
}

/**
 * Handles the "Project->Remake..." menu command.
 * Displays the make dialogue and runs the make command.
 */
void KScope::slotProjectRemake()
{
	// Make sure the make dialogue exists and is displayed
	slotProjectMake();
	
	// Run the make command
	m_pMakeDlg->slotMake();
}

/**
 * Handles the "Go->Global Bookmarks" menu command.
 * Displays a dialogue with the set of all bookmarks currently set in this
 * project.
 */
void KScope::slotShowBookmarks()
{
	BookmarksDlg dlg;
	QString sPath;
	uint nLine;
	
	// Load the bookmark list
	m_pEditTabs->showBookmarks(dlg.getView());
	
	// Show the dialogue
	if (dlg.exec() != QDialog::Accepted)
		return;
	
	// Go to the selected bookmark
	dlg.getBookmark(sPath, nLine);
	slotShowEditor(sPath, nLine);
}

/**
 * Prompts the user for a symbol to query.
 * Shows a dialog with a line edit widget, where the user can enter a symbol
 * on which to query Cscope. The meaning of the symbol depends on the type of
 * query.
 * @param	nType	The requested type of query (may be changed in the
 *					dialogue)
 * @param	sSymbol	Holds the requested symbol, upon successful return
 * @param	bPrompt	If false, the user is prompted only if a symbol cannot be
 *					determined automatically
 * @return	true if the user hs enetered a symbol, false otherwise
 */
bool KScope::getSymbol(uint& nType, QString& sSymbol, bool& bCase,
	bool bPrompt)
{
	EditorPage* pPage;
	QString sSuggested;
	
	// Set the currently selected text, if any
	if ((pPage = m_pEditTabs->getCurrentPage()) != NULL)
		sSuggested = pPage->getSuggestedText();

	// Return if a symbol was found, and prompting is turned off
	if (!sSuggested.isEmpty() && !bPrompt) {
		sSymbol = sSuggested;
		return true;
	}
	
	// Show the symbol dialogue
	sSymbol = SymbolDlg::promptSymbol(this, nType, sSuggested, bCase);

	// Cannot accept empty strings
	if (sSymbol.isEmpty())
		return false;
	
	return true;
}

/**
 * Opens a file in a new editor tab.
 * If an editor page already exists for the requested file, it is selected.
 * Otherwise, a new page is created, and the requested file is loaded.
 * @param	sFilePath	The path of the file to open
 * @return	A pointer to the found or newly created editor page
 */
EditorPage* KScope::addEditor(const QString& sFilePath)
{
	EditorPage* pPage;
	QString sAbsFilePath;
	ProjectBase* pProj;
	
	// If the file name is given using a relative path, we need to convert
	// it to an absolute one
	// TODO: Project needs a translatePath() method
	pProj = m_pProjMgr->curProject();
	if (sFilePath[0] != '/' && pProj) {
		sAbsFilePath = QDir::cleanDirPath(pProj->getSourceRoot() + "/" +
			sFilePath);
	}
	else {
		sAbsFilePath = QDir::cleanDirPath(sFilePath);
	}
	
	// Do not open a new editor if one exists for this file
	pPage = m_pEditTabs->findEditorPage(sAbsFilePath);
	if (pPage != NULL)
		return pPage;

	// Create a new page
	pPage = createEditorPage();	
				
	// Open the requested file
	pPage->open(sAbsFilePath);
	
	return pPage;
}

/**
 * Creates a new editor page, and adds it to the editors tab widget.
 * @return	A pointer to the new page
 */
EditorPage* KScope::createEditorPage()
{
	KTextEditor::Document* pDoc;
	EditorPage* pPage;
	QPopupMenu* pMenu;
	ProjectBase* pProj;
	
	// Load a new document part
	pDoc = m_pEditMgr->add();
	if (pDoc == NULL)
		return NULL;

	// Create the new editor page
	pMenu = (QPopupMenu*)factory()->container(Config().getEditorPopupName(),
		this);
	pPage = new EditorPage(pDoc, pMenu, m_pEditTabs);
	m_pEditTabs->addEditorPage(pPage);

	// Show the file's path in the main title
	connect(pPage, SIGNAL(fileOpened(EditorPage*, const QString&)), this,
		SLOT(slotFileOpened(EditorPage*, const QString&)));

	// Show cursor position in the status bar
	connect(pPage, SIGNAL(cursorPosChanged(uint, uint)), this,
		SLOT(slotShowCursorPos(uint, uint)));
	
	// Rebuild the database after a file has changed
	connect(pPage, SIGNAL(fileSaved(const QString&, bool)), this,
		SLOT(slotFileSaved(const QString&, bool)));
	
	// Handle file drops
	connect(pPage->getView(), SIGNAL(dropEventPass(QDropEvent*)), this,
		SLOT(slotDropEvent(QDropEvent*)));

	// Apply per-project configuration
	pProj = m_pProjMgr->curProject();
	if (pProj && pProj->getTabWidth() > 0)
		pPage->setTabWidth(pProj->getTabWidth());
	
	return pPage;
}
	
/**
 * @return	true if database auto-rebuild is enabled for the current project,
 *			false otherwise
 */
inline bool KScope::isAutoRebuildEnabled()
{
	ProjectBase* pProj;
	
	pProj = m_pProjMgr->curProject();
	return (pProj && pProj->getAutoRebuildTime() >= 0);
}

/**
 * Deletes an editor page after it has been removed.
 * The document object associated with the page is removed from the part 
 * manager, and the view object is removed from the GUI manager.
 * This slot is connected to the editorRemoved() signal of the EditorTabs 
 * object.
 * @param	pPage	The editor page to delete
 */
void KScope::slotDeleteEditor(EditorPage* pPage)
{
	guiFactory()->removeClient(pPage->getView());
	m_pEditMgr->remove(pPage->getDocument());
	delete pPage;
}

/**
 * Sets an editor part as active when its owner tab is chosen.
 * Whenever a different editor tab is chosen, its editor part should become
 * the active part. This means that this part's GUI is merged with the
 * application's, and that it responds to actions.
 * @param	pOldPage	The editor page that has ceased to be active
 * @param	pNewPage	The newly chosen editor page
 */
void KScope::slotChangeEditor(EditorPage* pOldPage, EditorPage* pNewPage)
{
	KXMLGUIFactory* pFactory = guiFactory();
	
	// Remove the current GUI
	if (pOldPage)
		pFactory->removeClient(pOldPage->getView());

	// Set the new active part and create its GUI
	if (m_bUpdateGUI && pNewPage) {
		m_pEditMgr->setActivePart(pNewPage->getDocument());
		pFactory->addClient(pNewPage->getView());
		m_sCurFilePath = pNewPage->getFilePath();
		setCaption(m_pProjMgr->getProjName() + " - " + m_sCurFilePath);
	}
	
	// Enable/disable file-related actions, if necessary
	if (pOldPage && !pNewPage)
		m_pActions->slotEnableFileActions(false);
	else if (!pOldPage && pNewPage)
		m_pActions->slotEnableFileActions(true);
}

/**
 * Opens an editor for the given file and sets the cursor to the beginning of 
 * the requested line.
 * @param	sFilePath	The full path of the file to open for editing
 * @param	nLine		The number of the line on which to position the
 *						cursor, or 0 to maintain the cursor in its current
 *						position (which does not affect the position history)
 */
void KScope::slotShowEditor(const QString& sFilePath, uint nLine)
{
	EditorPage* pPage;

	// Save current position in the position history
	if (nLine != 0 && (pPage = m_pEditTabs->getCurrentPage())) {
		m_pQueryWidget->addHistoryRecord(m_sCurFilePath, m_nCurLine,
			pPage->getLineContents(m_nCurLine));
	}
	
	// Open the requested file (or select an already-open editor page)
	pPage = addEditor(sFilePath);
	if (pPage == NULL)
		return;
	
	// Make sure the main window is visible
	raise();
	setWindowState(windowState() & ~WindowMinimized | WindowActive);
	
	if (nLine != 0) {
		// Set the cursor to the requested line
		pPage->slotGotoLine(nLine);
	
		// Add the new position to the position history
		m_pQueryWidget->addHistoryRecord(m_sCurFilePath, m_nCurLine,
			pPage->getLineContents(m_nCurLine));
	}
}

/**
 * A wrapper around slotShowEditor, that enables auto-hiding of the query
 * widget after a query result has been chosen.
 * This slot is connected to the lineRequested() signal emitted by a QueryPage
 * object.
 * @param	sFilePath	The full path of the file to open for editing
 * @param	nLine		The number of the line on which to position the cursor
 */
void KScope::slotQueryShowEditor(const QString& sFilePath, uint nLine)
{
	// Hide the query window, if it was hidden before a query was initiated
	if (m_bHideQueryOnSelection)
		toggleQueryWindow(false);
	
	// Open an editor at the requested line
	slotShowEditor(sFilePath, nLine);
}

/**
 * Handles the "Go->Position History" menu command.
 * Ensures that the query window is visible, and selects the active history
 * page.
 */
void KScope::slotHistoryShow()
{
	toggleQueryWindow(true);
	m_pQueryWidget->selectActiveHistory();
}

/**
 * Handles the "File->New" menu command.
 * Creates an editor page for a new unnamed file.
 */
void KScope::slotNewFile()
{
	EditorPage* pPage;

	// Create the new editor page
	pPage = createEditorPage();
	
	// Mark the page as containing a new file
	pPage->setNewFile();
}

/**
 * Handles the "File->Open" menu command.
 * Prompts the user for a file name, and opens it in a new editor page.
 */
void KScope::slotOpenFile()
{
	ProjectBase* pProj;
	QStringList slFiles;
	QStringList::Iterator itr;
	
	// Prompt the user for the file(s) to open.
	pProj = m_pProjMgr->curProject();
	slFiles = KFileDialog::getOpenFileNames(pProj ? pProj->getSourceRoot() : 
		QString::null);
	
	// Open all selected files.
	for (itr = slFiles.begin(); itr != slFiles.end(); ++itr) {
		if (!(*itr).isEmpty())
			slotShowEditor(*itr, 0);
	}
}

/**
 * Handles the "File->Close" menu command.
 * Closes the currently active editor page.
 */
void KScope::slotCloseEditor()
{
	m_pEditTabs->removeCurrentPage();
}

/**
 * Handles the "Window->Close All" menu command.
 * Closes all open editor pages.
 */
void KScope::slotCloseAllWindows()
{
	m_bUpdateGUI = false;
	m_pEditTabs->removeAllPages();
	m_bUpdateGUI = true;
}

/**
 * Displays error messages from a Cscope process.
 * This slot is connected to the progress() signal emitted by the any
 * Cscope process.
 * @param	sMsg	The error message
 */
void KScope::slotCscopeError(const QString& sMsg)
{
	m_pMsgDlg->addText(sMsg);
}

/**
 * Reports progress information from the Cscope process responsible for
 * rebuilding the cross-reference database.
 * This slot is connected to the progress() signal emitted by the builder
 * process.
 * Progress information is displayed in the status bar.
 * @param	nFiles	The number of files scanned
 * @param	nTotal	The total number of files in the project
 */
void KScope::slotBuildProgress(int nFiles, int nTotal)
{
	QString sMsg;
	
	// Use the progress dialogue, if it exists (first time builds)
	if (m_pProgressDlg) {
		m_pProgressDlg->setValue((nFiles * 100) / nTotal);
		return;
	}
	
	// Show progress information
	sMsg = i18n("Rebuilding the cross reference database...") + " " +
		QString::number((nFiles * 100) / nTotal) + "%";
	statusBar()->message(sMsg);
}

/**
 * Reports to the user that Cscope has started building the inverted index.
 * This slot is connected to the buildInvIndex() signal emitted by the 
 * builder process.
 */
void KScope::slotBuildInvIndex()
{
	if (m_pProgressDlg) {
		m_pProgressDlg->setLabel(i18n("Please wait while KScope builds the "
			"inverted index"));
		m_pProgressDlg->setIdle();
		return;
	}
	
	statusBar()->message(i18n("Rebuilding inverted index..."));
}

/**
 * Informs the user the database rebuild process has finished.
 * This slot is connected to the finished() signal emitted by the builder
 * process.
 */
void KScope::slotBuildFinished(uint)
{
	// Delete the progress dialogue, if it exists (first time builds)
	if (m_pProgressDlg) {
		delete m_pProgressDlg;
		m_pProgressDlg = NULL;
		return;
	}
	
	// Show a message in the status bar
	statusBar()->message(i18n("Rebuilding the cross reference database..."
		"Done!"), 3000);
}

/**
 * Called if the build process failed to complete.
 * This slot is connected to the aborted() signal emitted by the builder
 * process.
 */
void KScope::slotBuildAborted()
{
	// Delete the progress dialogue, if it exists (first time builds)
	if (m_pProgressDlg) {
		delete m_pProgressDlg;
		m_pProgressDlg = NULL;
	
		// Display a failure message
		KMessageBox::error(0, i18n("The database could not be built.\n"
			"Cross-reference information will not be available for this "
			"project.\n"
			"Please ensure that the Cscope parameters were correctly "
			"entered in the \"Settings\" dialogue."));		
		return;
	}
	
	// Show a message in the status bar
	statusBar()->message(i18n("Rebuilding the cross reference database..."
		"Failed"), 3000);	
}

/**
 * Applies the selected user preferences once the "Apply" or "OK" buttons in
 * the preferences dialog is clicked.
 */
void KScope::slotApplyPref()
{
	m_pQueryWidget->applyPrefs();
	m_pFileList->applyPrefs();
	m_pEditTabs->applyPrefs();
	m_pEditMgr->applyPrefs();

	// Enable/disable the external editor menu item
	m_pActions->enableExtEditor(Config().useExtEditor());
}

/**
 * Displays the current cursor position, whenever it is moved by the user.
 * This slot is connected to the cursorPosChanged() signal emitted by an
 * EditorPage object.
 * @param	nLine	The new line number
 * @param	nCol	The new column number
 */
void KScope::slotShowCursorPos(uint nLine, uint nCol)
{
	KStatusBar* pStatus = statusBar();
	QString sText;
	
	/* Show the line and column numbers. */
	QTextOStream(&sText) << " Line: " << nLine << " Col: " << nCol << " ";
	pStatus->changeItem(sText, 0);
	
	/* Store the current line. */
	m_nCurLine = nLine;
}

/**
 * Stores the path of a newly opened file.
 * This slot is connected to the fileOpened() signal emitted by an
 * EditorPage object.
 * @param	sFilePath	The full path of the opened file
 */
void KScope::slotFileOpened(EditorPage*, const QString& sFilePath)
{
	m_sCurFilePath = sFilePath;
	setCaption(m_pProjMgr->getProjName() + " - " + m_sCurFilePath);
}

/**
 * Sets a timer for rebuilding the database after a file has been saved.
 * This slot is connected to the fileSaved() signal emitted by an EditorPage
 * object.
 * The time period before rebuilding is determined on a per-project basis.
 * @param	sPath	The full path of the modified file that caused this event
 * @param	bIsNew	true if this is a new file, false otherwise
 */
void KScope::slotFileSaved(const QString& sPath, bool bIsNew)
{
	ProjectBase* pProj;
	int nTime;
	
	pProj = m_pProjMgr->curProject();
	if (!pProj)
		return;
	
	// Prompt the user to add this file to the current project
	if (bIsNew && !pProj->isTemporary()) {
		if (KMessageBox::questionYesNo(0, 
			i18n("Whould you like to add this file to the active project?")) == 
				  KMessageBox::Yes) {
			
			// Add the path to the 'cscope.files' file
			if (!((Project*)pProj)->addFile(sPath)) {
				KMessageBox::error(0, i18n("Failed to write the file list."));
				return;
			}
			
			// Add the path to the file list widget
			m_pFileList->addItem(sPath);
			
			// Rebuild immediately
			slotRebuildDB();
			return;
		}
	}
	
	// Get the project's auto-rebuild time
	nTime = pProj->getAutoRebuildTime();
	
	// Do nothing if the time is set to -1
	if (nTime == -1)
		return;
		
	// Check if the file is included in the project (external files should
	// not trigger the timer)
	if (!m_pFileList->findFile(sPath))
		return;
	
	// Rebuild immediately for a time set to 0
	if (nTime == 0) {
		slotRebuildDB();
		return;
	}

	// Reset the rebuild timer
	m_timerRebuild.start(nTime * 1000, true);
}

/**
 * Handles file drops inside the editors tab widget.
 * Opens all files dropped over the widget.
 * @param	pEvent	Pointer to an object containing the list of dropped files
 */
void KScope::slotDropEvent(QDropEvent* pEvent)
{
	KURL::List list;
	KURL::List::Iterator itr;
		
	// Create a list of file URLs
	if (!KURLDrag::decode(pEvent, list))
		return;
		
	// Open all files in the list
	for (itr = list.begin(); itr != list.end(); ++itr)
		addEditor((*itr).path());
}

#include "kscope.moc"
