diff options
Diffstat (limited to 'parts/grepview/grepviewwidget.cpp')
-rw-r--r-- | parts/grepview/grepviewwidget.cpp | 535 |
1 files changed, 535 insertions, 0 deletions
diff --git a/parts/grepview/grepviewwidget.cpp b/parts/grepview/grepviewwidget.cpp new file mode 100644 index 00000000..326e2897 --- /dev/null +++ b/parts/grepview/grepviewwidget.cpp @@ -0,0 +1,535 @@ +/*************************************************************************** + * Copyright (C) 1999-2001 by Bernd Gehrmann * + * bernd@kdevelop.org * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include <qdir.h> +#include <qlayout.h> +#include <qregexp.h> +#include <qpainter.h> +#include <qtoolbutton.h> +#include <kdialogbase.h> +#include <klocale.h> +#include <kprocess.h> +#include <kparts/part.h> +#include <ktexteditor/selectioninterface.h> +#include <kaction.h> +#include <kpopupmenu.h> +#include <ktabwidget.h> +#include <kpushbutton.h> +#include <kiconloader.h> +#include <kmessagebox.h> +using namespace KTextEditor; + +#include "kdevcore.h" +#include "kdevproject.h" +#include "kdevmainwindow.h" +#include "kdevpartcontroller.h" + +#include "grepdlg.h" +#include "grepviewpart.h" +#include "grepviewwidget.h" + + +class GrepListBoxItem : public ProcessListBoxItem +{ +public: + GrepListBoxItem(const QString &fileName, const QString &lineNumber, const QString &text, bool showFilename); + QString filename() + { return fileName; } + int linenumber() + { return lineNumber.toInt(); } + virtual bool isCustomItem(); + +private: + virtual void paint(QPainter *p); + QString fileName, lineNumber, text; + bool show; +}; + + +GrepListBoxItem::GrepListBoxItem(const QString &fileName, const QString &lineNumber, const QString &text, bool showFilename) + : ProcessListBoxItem( QString::null, Normal), + fileName(fileName), lineNumber(lineNumber), text(text.stripWhiteSpace()), + show(showFilename) +{ + this->text.replace( QChar('\t'), QString(" ") ); +} + + +bool GrepListBoxItem::isCustomItem() +{ + return true; +} + + +void GrepListBoxItem::paint(QPainter *p) +{ + QColor base, dim, result, bkground; + if (listBox()) { + const QColorGroup& group = listBox()->palette().active(); + if (isSelected()) { + bkground = group.button(); + base = group.buttonText(); + } + else + { + bkground = group.base(); + base = group.text(); + } + dim = blend(base, bkground); + result = group.link(); + } + else + { + base = Qt::black; + dim = Qt::darkGreen; + result = Qt::blue; + if (isSelected()) + bkground = Qt::lightGray; + else + bkground = Qt::white; + } + QFontMetrics fm = p->fontMetrics(); + QString stx = lineNumber + ": "; + int y = fm.ascent()+fm.leading()/2; + int x = 3; + p->fillRect(p->window(), QBrush(bkground)); + if (show) + { + p->setPen(dim); + p->drawText(x, y, fileName); + x += fm.width(fileName); + } + else + { + p->setPen(base); + QFont font1(p->font()); + QFont font2(font1); + font2.setBold(true); + p->setFont(font2); + p->drawText(x, y, stx); + p->setFont(font1); + x += fm.width(stx); + + p->setPen(result); + p->drawText(x, y, text); + } +} + + +GrepViewWidget::GrepViewWidget(GrepViewPart *part) : QWidget(0, "grepview widget") +{ + m_layout = new QHBoxLayout(this, 0, -1, "greplayout"); + + m_tabWidget = new KTabWidget(this); + + m_layout->add(m_tabWidget); + + m_curOutput = new GrepViewProcessWidget(m_tabWidget); + + m_tabWidget->addTab(m_curOutput, i18n("Search Results")); + + grepdlg = new GrepDialog( part, this, "grep widget"); + connect( grepdlg, SIGNAL(searchClicked()), this, SLOT(searchActivated()) ); + connect( m_curOutput, SIGNAL(processExited(KProcess* )), this, SLOT(slotSearchProcessExited()) ); + + connect( m_tabWidget, SIGNAL(currentChanged(QWidget*)), this, SLOT(slotOutputTabChanged()) ); + + connect( m_curOutput, SIGNAL(clicked(QListBoxItem*)), + this, SLOT(slotExecuted(QListBoxItem*)) ); + connect( m_curOutput, SIGNAL(returnPressed(QListBoxItem*)), + this, SLOT(slotExecuted(QListBoxItem*)) ); + + + connect( m_curOutput, SIGNAL(contextMenuRequested( QListBoxItem*, const QPoint&)), this, SLOT(popupMenu(QListBoxItem*, const QPoint&))); + m_part = part; + + m_closeButton = new QToolButton(m_tabWidget);//@todo change text/icon + m_closeButton->setIconSet(SmallIconSet("tab_remove")); + m_closeButton->setEnabled(false); + + connect (m_closeButton, SIGNAL(clicked()), this, SLOT(slotCloseCurrentOutput())); + m_tabWidget->setCornerWidget(m_closeButton); +} + + +GrepViewWidget::~GrepViewWidget() +{} + + +void GrepViewWidget::showDialog() +{ + // Get the selected text if there is any + KParts::ReadOnlyPart *ro_part = dynamic_cast<KParts::ReadOnlyPart*>(m_part->partController()->activePart()); + if (ro_part) + { + SelectionInterface *selectIface = dynamic_cast<SelectionInterface*>(ro_part); + if(selectIface && selectIface->hasSelection()) + { + QString selText = selectIface->selection(); + if(!selText.contains('\n')) + { + grepdlg->setPattern(selText); + } + } + } + // Determine if we have a list of project files + KDevProject *openProject = m_part->project(); + if (openProject) + { + grepdlg->setEnableProjectBox(!openProject->allFiles().isEmpty()); + } + else + { + grepdlg->setEnableProjectBox(false); + } + grepdlg->show(); +} + +// @todo - put this somewhere common - or just use Qt if possible +static QString escape(const QString &str) +{ + QString escaped("[]{}()\\^$?.+-*|"); + QString res; + + for (uint i=0; i < str.length(); ++i) + { + if (escaped.find(str[i]) != -1) + res += "\\"; + res += str[i]; + } + + return res; +} + + +void GrepViewWidget::showDialogWithPattern(QString pattern) +{ + // Before anything, this removes line feeds from the + // beginning and the end. + int len = pattern.length(); + if (len > 0 && pattern[0] == '\n') + { + pattern.remove(0, 1); + len--; + } + if (len > 0 && pattern[len-1] == '\n') + pattern.truncate(len-1); + grepdlg->setPattern( pattern ); + + // Determine if we have a list of project files + KDevProject *openProject = m_part->project(); + if (openProject) + { + grepdlg->setEnableProjectBox(!openProject->allFiles().isEmpty()); + } + else + { + grepdlg->setEnableProjectBox(false); + } + grepdlg->show(); +} + + +void GrepViewWidget::searchActivated() +{ + if ( grepdlg->keepOutputFlag() ) + slotKeepOutput(); + + m_tabWidget->showPage( m_curOutput ); + + m_curOutput->setLastFileName(""); + m_curOutput->setMatchCount( 0 ); + + QString command, files; + + // waba: code below breaks on filenames containing a ',' !!! + QStringList filelist = QStringList::split(",", grepdlg->filesString()); + + if (grepdlg->useProjectFilesFlag()) + { + KDevProject *openProject = m_part->project(); + if (openProject) + { + QString tmpFilePath; + QStringList projectFiles = openProject->allFiles(); + if (!projectFiles.isEmpty()) + { + tmpFilePath = openProject->projectDirectory() + QChar(QDir::separator()) + ".grep.tmp"; + QString dir = grepdlg->directoryString(), file; + QValueList<QRegExp> regExpList; + + if (dir.endsWith(QChar(QDir::separator()))) + dir.truncate(dir.length() - 1); + + if (!filelist.isEmpty()) + { + for (QStringList::Iterator it = filelist.begin(); it != filelist.end(); ++it) + regExpList.append(QRegExp(*it, true, true)); + } + + m_tempFile.setName(tmpFilePath); + if (m_tempFile.open(IO_WriteOnly)) + { + QTextStream out(&m_tempFile); + for (QStringList::Iterator it = projectFiles.begin(); it != projectFiles.end(); ++it) + { + file = QDir::cleanDirPath(openProject->projectDirectory() + QChar(QDir::separator()) + *it); + + QFileInfo info(file); + if (grepdlg->recursiveFlag() && !info.dirPath(true).startsWith(dir)) continue; + if (!grepdlg->recursiveFlag() && info.dirPath(true) != dir) continue; + + bool matchOne = regExpList.count() == 0; + for (QValueList<QRegExp>::Iterator it2 = regExpList.begin(); it2 != regExpList.end() && !matchOne; ++it2) + matchOne = (*it2).exactMatch(file); + + if (matchOne) + out << KShellProcess::quote(file) + "\n"; + } + + m_tempFile.close(); + } + else + { + KMessageBox::error(this, i18n("Unable to create a temporary file for search.")); + return; + } + } + + command = "cat "; + command += tmpFilePath.replace(' ', "\\ "); + } + } + else + { + if (!filelist.isEmpty()) + { + QStringList::Iterator it(filelist.begin()); + files = KShellProcess::quote(*it); + ++it; + for (; it != filelist.end(); ++it) + files += " -o -name " + KShellProcess::quote(*it); + } + + QString filepattern = "find "; + filepattern += KShellProcess::quote(grepdlg->directoryString()); + if (!grepdlg->recursiveFlag()) + filepattern += " -maxdepth 1"; + filepattern += " \\( -name "; + filepattern += files; + filepattern += " \\) -print -follow"; + if (grepdlg->noFindErrorsFlag()) + filepattern += " 2>/dev/null"; + + command = filepattern + " " ; + } + + QStringList excludelist = QStringList::split(",", grepdlg->excludeString()); + if (!excludelist.isEmpty()) + { + QStringList::Iterator it(excludelist.begin()); + command += "| grep -v "; + for (; it != excludelist.end(); ++it) + command += "-e " + KShellProcess::quote(*it) + " "; + } + + if (!grepdlg->useProjectFilesFlag()) + { + // quote spaces in filenames going to xargs + command += "| sed \"s/ /\\\\\\ /g\" "; + } + + command += "| xargs " ; + +#ifndef USE_SOLARIS + command += "egrep -H -n -s "; +#else + // -H reported as not being available on Solaris, + // but we're buggy without it on Linux. + command += "egrep -n "; +#endif + + if (!grepdlg->caseSensitiveFlag()) + { + command += "-i "; + } + command += "-e "; + + m_lastPattern = grepdlg->patternString(); + QString pattern = grepdlg->templateString(); + if (grepdlg->regexpFlag()) + pattern.replace(QRegExp("%s"), grepdlg->patternString()); + else + pattern.replace(QRegExp("%s"), escape( grepdlg->patternString() ) ); + command += KShellProcess::quote(pattern); + m_curOutput->startJob("", command); + + m_part->mainWindow()->raiseView(this); + m_part->core()->running(m_part, true); +} + + +void GrepViewWidget::slotExecuted(QListBoxItem* item) +{ + ProcessListBoxItem *i = static_cast<ProcessListBoxItem*>(item); + if (!i || !i->isCustomItem()) + return; + + GrepListBoxItem *gi = static_cast<GrepListBoxItem*>(i); + m_part->partController()->editDocument( KURL( gi->filename() ), gi->linenumber()-1 ); +} + + +void GrepViewProcessWidget::insertStdoutLine(const QCString &line) +{ + int pos; + QString filename, linenumber, rest; + + QString str; + if( !grepbuf.isEmpty() ) + { + str = QString::fromLocal8Bit( grepbuf+line ); + grepbuf.truncate( 0 ); + }else + { + str = QString::fromLocal8Bit( line ); + } + if ( (pos = str.find(':')) != -1) + { + filename = str.left(pos); + str.remove( 0, pos+1 ); + if ( ( pos = str.find(':') ) != -1) + { + linenumber = str.left(pos); + str.remove( 0, pos+1 ); + // filename will be displayed only once + // selecting filename will display line 1 of file, + // otherwise, line of requested search + if ( _lastfilename != filename ) + { + _lastfilename = filename; + insertItem(new GrepListBoxItem(filename, "0", str, true)); + insertItem(new GrepListBoxItem(filename, linenumber, str, false)); + } + else + { + insertItem(new GrepListBoxItem(filename, linenumber, str, false)); + } + maybeScrollToBottom(); + } + m_matchCount++; + } +} + + +void GrepViewWidget::projectChanged(KDevProject *project) +{ + QString dir = project? project->projectDirectory() : QDir::homeDirPath(); + grepdlg->setDirectory(dir); +} + +void GrepViewWidget::popupMenu(QListBoxItem*, const QPoint& p) +{ + if(m_curOutput->isRunning()) return; + + KPopupMenu rmbMenu; + + if(KAction *findAction = m_part->actionCollection()->action("edit_grep")) + { + rmbMenu.insertTitle(i18n("Find in Files")); + findAction->plug(&rmbMenu); + rmbMenu.exec(p); + } +} + +void GrepViewWidget::slotKeepOutput( ) +{ + if ( m_lastPattern == QString::null ) return; + + m_tabWidget->changeTab(m_curOutput, m_lastPattern); + + m_curOutput = new GrepViewProcessWidget(m_tabWidget); + m_tabWidget->insertTab(m_curOutput, i18n("Search Results"), 0); + + connect( m_curOutput, SIGNAL(clicked(QListBoxItem*)), this, SLOT(slotExecuted(QListBoxItem*)) ); + connect( m_curOutput, SIGNAL(returnPressed(QListBoxItem*)), this, SLOT(slotExecuted(QListBoxItem*)) ); + connect( m_curOutput, SIGNAL(processExited(KProcess* )), this, SLOT(slotSearchProcessExited()) ); + connect( m_curOutput, SIGNAL(contextMenuRequested( QListBoxItem*, const QPoint&)), this, SLOT(popupMenu(QListBoxItem*, const QPoint&))); +} + +void GrepViewWidget::slotCloseCurrentOutput( ) +{ + ProcessWidget* pw = static_cast<ProcessWidget*>(m_tabWidget->currentPage()); + if (pw == m_curOutput) + return; + + m_tabWidget->removePage(pw); + delete pw; + + if (m_tabWidget->count() == 1) + m_closeButton->setEnabled( false ); +} + +void GrepViewWidget::killJob( int signo ) +{ + m_curOutput->killJob( signo ); + + if (!m_tempFile.name().isEmpty() && m_tempFile.exists()) + m_tempFile.remove(); +} + +bool GrepViewWidget::isRunning( ) const +{ + return m_curOutput->isRunning(); +} + +void GrepViewWidget::slotOutputTabChanged( ) +{ + ProcessWidget* pw = static_cast<ProcessWidget*>(m_tabWidget->currentPage()); + if (pw == m_curOutput) + m_closeButton->setEnabled( false ); + else + m_closeButton->setEnabled( true ); +} + +void GrepViewWidget::slotSearchProcessExited( ) +{ + m_part->core()->running(m_part, false); + + if (!m_tempFile.name().isEmpty() && m_tempFile.exists()) + m_tempFile.remove(); +} + +void GrepViewProcessWidget::childFinished( bool normal, int status ) +{ + // When xargs executes grep several times (because the command line + // generated would be too large for a single grep) and one of those + // sets of files passed to grep does not contain a match, then an + // error status of 123 is set by xargs, even if there is a match in + // another set of files. + // Reset this false status here. + + if (status == 123 && numRows() > 1) + status = 0; + + insertItem(new ProcessListBoxItem(i18n("*** %n match found. ***", "*** %n matches found. ***", m_matchCount), ProcessListBoxItem::Diagnostic)); + maybeScrollToBottom(); + + ProcessWidget::childFinished(normal, status); +} + +void GrepViewProcessWidget::addPartialStdoutLine(const QCString & line) +{ + grepbuf += line; +} + + +#include "grepviewwidget.moc" |