You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
kscope/src/kscope.cpp

1759 lines
46 KiB

/***************************************************************************
*
* 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())
{
Config().loadDefault();
Config().store();
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"