Logo Search packages:      
Sourcecode: kdeaddons version File versions

plugin_katemake.cpp

/* plugin_katemake.h                    Kate Plugin
**
** Copyright (C) 2003 by Adriaan de Groot
**
** This is the hader for the Make plugin.
**
** This code was mostly copied from the GPL'ed xmlcheck plugin
** by Daniel Naber.
*/

/*
** 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.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program in a file called COPYING; if not, write to
** the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
** MA 02111-1307, USA.
*/

#include "plugin_katemake.moc"

#include <cassert>

#include <config.h>
#include <qfile.h>
#include <qfileinfo.h>
#include <qinputdialog.h>
#include <qregexp.h>
#include <qstring.h>
#include <qtextstream.h>
#include <qpalette.h>
#include <qvbox.h>
#include <qlabel.h>

#include <kaction.h>
#include <kcursor.h>
#include <kdebug.h>
#include <kdockwidget.h>
#include <kinstance.h>
#include <klocale.h>
#include <kmessagebox.h>
#include <kstandarddirs.h>
#include <kpassivepopup.h>
#include <klineedit.h>
#include <kdialogbase.h>
#include <kconfig.h>

#include <kate/toolviewmanager.h>

#include <kgenericfactory.h>

K_EXPORT_COMPONENT_FACTORY( katemakeplugin, KGenericFactory<PluginKateMake>( "katemake" ) )

// #define FUNCTIONSETUP kdDebug() << k_funcinfo << endl;
#define FUNCTIONSETUP

PluginKateMake::PluginKateMake( QObject* parent, const char* name, const QStringList& )
      : Kate::Plugin ( (Kate::Application *)parent, name )
{
      FUNCTIONSETUP;
}


PluginKateMake::~PluginKateMake()
{
      FUNCTIONSETUP;
}


void PluginKateMake::addView(Kate::MainWindow *win)
{
      FUNCTIONSETUP;

      PluginKateMakeView *view = new PluginKateMakeView (0,win,
            "katemakeview");
      if( ! view ) {
            kdDebug() << "Error: no plugin view" << endl;
            return;
      }

      if( ! win ) {
            kdDebug() << "Error: no main win" << endl;
            return;
      }

      win->guiFactory()->addClient(view);
      view->win = win;
      m_views.append(view);

      Kate::ToolViewManager *viewmanager = win->toolViewManager();
      viewmanager->addToolView(KDockWidget::DockBottom,
            view, SmallIcon(QString::fromLatin1("misc")),
            i18n("Make Output"));
}


void PluginKateMake::removeView(Kate::MainWindow *win)
{
      FUNCTIONSETUP;

      for (unsigned int z=0; z < m_views.count(); z++) {
            if (m_views.at(z)->win == win) {
                  PluginKateMakeView *view = m_views.at(z);
                  m_views.remove (view);
                  win->guiFactory()->removeClient (view);
                  Kate::ToolViewManager *viewmanager = win->toolViewManager();
                  viewmanager->removeToolView(view);
            }
      }
}

#define COL_LINE  (1)
#define COL_FILE  (0)
#define COL_MSG         (2)


class ErrorMessage : public QListViewItem
{
public:
      ErrorMessage(QListView *parent,
            const QString &filename,
            int lineno,
            const QString &message) :
            QListViewItem(parent,
                  filename,
                  (lineno > 0 ? QString::number(lineno) : QString::null),
                  message)
      {
            m_isError = !message.contains(QString::fromLatin1("warning"));
            m_lineno = lineno;
            m_serial = s_serial++;
      } ;
      ErrorMessage(QListView *parent,const QString &message) :
            QListViewItem(parent,QString::null,QString::null,QString::null)
      {
            QString m(message);
            m.remove('\n');
            m.stripWhiteSpace();
            setText(COL_MSG,m);

            m_isError=false;
            m_lineno=-1;
            m_serial = s_serial++;
            setSelectable(false);
      } ;
      ErrorMessage(QListView *parent, bool start) :
            QListViewItem(parent,QString::null)
      {
            m_isError=false;
            m_lineno=-1;
            m_serial=-1;
            setSelectable(false);
            if (start) setText(COL_MSG,i18n("Running make..."));
            else setText(COL_MSG,i18n("No Errors."));
      }
      virtual ~ErrorMessage() ;

      bool isError() const { return m_isError; } ;
      QString message() const { return text(COL_MSG); } ;
      QString fancyMessage() const;
      QString caption() const;
      QString filename() const { return text(COL_FILE); } ;
      int line() const { return m_lineno; } ;
      int serial() const { return m_serial; } ;

      virtual int compare(QListViewItem *,int,bool) const;

      static void resetSerial() { s_serial=10; } ;

protected:
      virtual void paintCell(QPainter *,const QColorGroup &,
            int,int,int);

      bool m_isError;
      int m_lineno;
      int m_serial;

      static int s_serial;
} ;

/* static */ int ErrorMessage::s_serial = 0;

/* virtual */ ErrorMessage::~ErrorMessage()
{
}

QString ErrorMessage::caption() const
{
      return QString::fromLatin1("%1:%2").arg(text(COL_FILE)).arg(line());
}

QString ErrorMessage::fancyMessage() const
{
      QString msg = QString::fromLatin1("<qt>");
      if (isError())
      {
            msg.append(QString::fromLatin1("<font color=\"red\">"));
      }
      msg.append(message());
      if (isError())
      {
            msg.append(QString::fromLatin1("</font>"));
      }
      msg.append(QString::fromLatin1("<qt>"));
      return msg;
}

/* virtual */ void ErrorMessage::paintCell(QPainter *p,
      const QColorGroup &cg,
      int column,
      int width,
      int align)
{
      if ((column!=COL_LINE) || (serial()<0))
      {
            QListViewItem::paintCell(p,cg,column,width,align);
      }
      else
      {
            QColorGroup myCG(cg);
#if 0
                  red,  //darkRed,
                  green,      //darkGreen,
                  blue,       //darkBlue,
                  cyan,       // darkCyan,
                  magenta, // darkMagenta,
                  yellow, //darkYellow,
                  gray);
#endif
            myCG.setColor(QColorGroup::Light,red);

            if (!isSelected())
            {
                  myCG.setColor(QColorGroup::Base,gray);
                  myCG.setColor(QColorGroup::Text,
                        m_isError ? red : yellow);
            }


            QListViewItem::paintCell(p,myCG,column,width,align);
      }
}

/* virtual */ int ErrorMessage::compare(QListViewItem *i,
      int /* column */ , bool /* ascending */) const
{
      kdDebug() << "In compare " << serial() << endl;
      ErrorMessage *e = dynamic_cast<ErrorMessage*>(i);
      if (!e) return 1;
      if (e->serial() < serial()) return 1;
      else if (e->serial() == serial()) return 0;
      else return -1;
}

class LinePopup : public KPassivePopup
{
protected:
      LinePopup( QWidget *parent=0, const char *name=0, WFlags f=0 );
      virtual ~LinePopup();

public:
      static LinePopup *message(QWidget *,
            const QPoint &p,ErrorMessage *e);

protected:
      virtual void positionSelf();

      QPoint fLoc;

      // There should be only one
      static LinePopup *one;
} ;

/* static */ LinePopup *LinePopup::one = 0L;

LinePopup::LinePopup(QWidget *p,const char *n,WFlags f) :
      KPassivePopup(p,n,f),
      fLoc(-1,-1)
{
      ASSERT(!one);
      one=this;
}

LinePopup::~LinePopup()
{
      one=0L;
}

/* static */ LinePopup *LinePopup::message(QWidget *parent,
      const QPoint &p,
      ErrorMessage *e)
{
      if (one) delete one;
      LinePopup *pop = new LinePopup( parent );
      pop->setAutoDelete( true );


      pop->setView( e->caption(), e->fancyMessage(), QPixmap() );
      // pop->hideDelay = timeout;
      pop->fLoc=p;
      pop->show();

      return pop;
}

/* virtual */ void LinePopup::positionSelf()
{
      if (fLoc.x()==-1) KPassivePopup::positionSelf();
      else
      {
            // Move above or below the intended line
            if (fLoc.y()>320) fLoc.setY(fLoc.y()-80);
            else fLoc.setY(fLoc.y()+80);
            moveNear(QRect(fLoc.x(),fLoc.y(),40,30));
      }
}

PluginKateMakeView::PluginKateMakeView(QWidget *parent,
      Kate::MainWindow *mainwin,
      const char* name) :
      QListView(parent,name),
      KXMLGUIClient(),
      win(mainwin),
      filenameDetector(0L),
      running_indicator(0L)
{
      FUNCTIONSETUP;

      m_proc=0;
      (void) new KAction ( i18n("Next Error"), KShortcut(ALT+CTRL+Key_Right),
            this, SLOT( slotNext() ),
            actionCollection(), "make_right" );

      (void) new KAction ( i18n("Previous Error"), KShortcut(ALT+CTRL+Key_Left),
            this, SLOT( slotPrev() ),
            actionCollection(), "make_left" );

      (void) new KAction ( i18n("Make"), KShortcut(ALT+Key_R),
            this, SLOT( slotValidate() ),
            actionCollection(), "make_check" );

      (void) new KAction ( i18n("Configure..."), KShortcut(),
            this, SLOT( slotConfigure() ),
            actionCollection(), "make_settings" );

      setInstance(new KInstance("kate"));
      setXMLFile(QString::fromLatin1("plugins/katemake/ui.rc"));


      setFocusPolicy(QWidget::NoFocus);
      setSorting(COL_LINE);

      addColumn(i18n("File"), -1);
      addColumn(i18n("Line"), -1);
      setColumnAlignment(COL_LINE, AlignRight);
      addColumn(i18n("Message"), -1);
      setAllColumnsShowFocus(true);
      setResizeMode(QListView::LastColumn);
      connect(this, SIGNAL(clicked(QListViewItem *)), SLOT(slotClicked(QListViewItem *)));

      m_proc = new KProcess();

      connect(m_proc, SIGNAL(processExited(KProcess*)), this, SLOT(slotProcExited(KProcess*)));
      connect(m_proc, SIGNAL(receivedStderr(KProcess*,char*,int)),
            this, SLOT(slotReceivedProcStderr(KProcess*, char*, int)));


      KConfig c("katemakepluginrc");
      c.setGroup("Prefixes");
      source_prefix = c.readEntry("Source",QString::null);
      build_prefix = c.readEntry("Build",QString::null);

      if (source_prefix.isEmpty())
      {
            filenameDetector = new QRegExp(
                  QString::fromLatin1("[a-zA-Z0-9_.-]*\\.[chp]*:[0-9]*:"));
      }
      else
      {
            filenameDetector = 0L;
      }
}


PluginKateMakeView::~PluginKateMakeView()
{
      FUNCTIONSETUP;

      delete m_proc;
      delete filenameDetector;
      delete running_indicator;
}


void PluginKateMakeView::processLine(const QString &l)
{
      kdDebug() << "Got line " << l ;


      if (!filenameDetector && l.find(source_prefix)!=0)
      {
            /* ErrorMessage *e = */ (void) new ErrorMessage(this,l);
            return;
      }

      if (filenameDetector && l.find(*filenameDetector)!=0)
      {
            ErrorMessage *e = new ErrorMessage(this,l);
            kdDebug() << "Got message #" << e->serial() << endl;
            return;
      }

      int ofs1 = l.find(':');
      int ofs2 = l.find(':',ofs1+1);
      //
      QString m = l.mid(ofs2+1);
      m.remove('\n');
      m.stripWhiteSpace();
      QString filename = l.left(ofs1);
      int line = l.mid(ofs1+1,ofs2-ofs1-1).toInt();
      ErrorMessage *e = new ErrorMessage(this,
            filename,line,m);
      kdDebug() << "Got message #" << e->serial() << endl;

      // Should cache files being found and check for
      // existence.
      // kdDebug() << ": Looking at " << document_dir+filename << endl;
      if (!QFile::exists(document_dir+filename))
      {
            e->setSelectable(false);
      }
      if (filename.startsWith(source_prefix) && !source_prefix.isEmpty())
      {
            e->setSelectable(true);
      }
      found_error=true;
}


void PluginKateMakeView::slotReceivedProcStderr(KProcess *, char *result, int len)
{
      FUNCTIONSETUP;

      QString l = QString::fromLocal8Bit( QCString(result, len+1) );

      output_line += l;

      int nl_p = -1;
      while ((nl_p = output_line.find('\n')) > 1)
      {
            processLine(output_line.left(nl_p+1));
            output_line.remove(0,nl_p+1);
      }
}


void PluginKateMakeView::slotProcExited(KProcess *p)
{
      FUNCTIONSETUP;

      delete running_indicator;
      running_indicator=0L;

      if (!output_line.isEmpty())
      {
            processLine(output_line);
      }

#if 0
      // FIXME: doesn't work correct the first time:
      if( m_dockwidget->isDockBackPossible() ) {
            m_dockwidget->dockBack();
      }
#endif

      kdDebug() << "slotProcExited()" << endl;
      QApplication::restoreOverrideCursor();

      sort();

      if ( found_error || !p->normalExit() || p->exitStatus() )
      {
            QListViewItem *i = firstChild();
            while (i && !i->isSelectable())
            {
                  i = i->nextSibling();
            }
            if (i)
            {
                  setSelected(i,true);
                  slotClicked(i);
            }
      }
      else
      {
            KPassivePopup::message(i18n("Make Results"),
                  i18n("No errors."),
                  this);
            clear();
#if 0
            QListViewItem *i = new QListViewItem(this,QString::null,
                  QString::null,
                  i18n("No Errors."));
            i->setSelectable(false);
#else
            (void) new ErrorMessage(this,false);
#endif
      }
}


void PluginKateMakeView::slotClicked(QListViewItem *item)
{
      FUNCTIONSETUP;
      if (!item)
      {
            kdDebug() << ": No item clicked." << endl;
            return;
      }
      if (!item->isSelectable()) return;

      ErrorMessage *e = dynamic_cast<ErrorMessage *>(item);
      if (!e) return;

      ensureItemVisible(e);

      QString filename = document_dir + e->filename();
      int lineno = e->line();

      if (!build_prefix.isEmpty())
      {
            filename = e->filename();
      }


      kdDebug() << ": Looking at " << filename 
            << ":" << lineno << endl;

      if (QFile::exists(filename))
      {
            KURL u;
            u.setPath(filename);
            win->viewManager()->openURL(u);
            Kate::View *kv = win->viewManager()->activeView();

            kv->setCursorPositionReal(lineno-1,1);


            QPoint globalPos = kv->mapToGlobal(kv->cursorCoordinates());
            kdDebug() << ": Want to map at "
                  << globalPos.x() << "," << globalPos.y() << endl;
#if 0
            KPassivePopup::message(
                  QString::fromLatin1("%1:%2").arg(filename).arg(lineno),
                  msg,
                  this);
#else
            LinePopup::message(this,globalPos,e);
#endif
      }
}

void PluginKateMakeView::slotNext()
{
      FUNCTIONSETUP;

      QListViewItem *i = selectedItem();
      if (!i) return;

      QListViewItem *n = i;
      while ((n=n->nextSibling()))
      {
            if (n->isSelectable())
            {
                  if (n==i) return;
                  setSelected(n,true);
                  ensureItemVisible(n);
                  slotClicked(n);
                  return;
            }
      }
}

void PluginKateMakeView::slotPrev()
{
      FUNCTIONSETUP;

      QListViewItem *i = selectedItem();
      if (!i) return;

      QListViewItem *n = i;
      while ((n=n->itemAbove()))
      {
            if (n->isSelectable())
            {
                  if (n==i) return;
                  setSelected(n,true);
                  ensureItemVisible(n);
                  slotClicked(n);
                  return;
            }
      }
}

bool PluginKateMakeView::slotValidate()
{
      FUNCTIONSETUP;

      clear();
      win->toolViewManager()->showToolView (this);

      m_proc->clearArguments();

      Kate::View *kv = win->viewManager()->activeView();
      if( ! kv ) {
            kdDebug() << "Error (slotValidate()): no Kate::View" << endl;
            return false;
      }
      if( ! kv->getDoc() ) {
            kdDebug() << "Error (slotValidate()): no kv->getDoc()" << endl;
            return false;
      }

      Kate::Document *doc = (Kate::Document*)kv->document();
      doc->save();
      KURL url(doc->url());

      output_line = QString::null;
      ErrorMessage::resetSerial();
      found_error=false;

      kdDebug() << ": Document " << url.protocol() << " : " <<
            url.path() << endl;

      if (!url.isLocalFile())
      {
            KMessageBox::sorry(0,
                  i18n("The file <i>%1</i> is not a local file. "
                        "Non-local files cannot be compiled.")
                        .arg(url.path()));
            return false;
      }

      document_dir = QFileInfo(url.path()).dirPath(true) +
            QString::fromLatin1("/");

      if (document_dir.startsWith(source_prefix))
      {
            document_dir = build_prefix + document_dir.mid(source_prefix.length());
      }

      m_proc->setWorkingDirectory(document_dir);
      *m_proc << "gmake" ;

      if( ! m_proc->start(KProcess::NotifyOnExit, KProcess::AllOutput) ) {
            KMessageBox::error(0, i18n("<b>Error:</b> Failed to run gmake."));
            return false;
      }
      QApplication::setOverrideCursor(KCursor::waitCursor());
      running_indicator = new ErrorMessage(this,true);
      return true;
}

class Settings : public KDialogBase
{
public:
      Settings( QWidget *parent,
            const QString &src, const QString &bld);

      KLineEdit *edit_src,*edit_bld;
} ;


Settings::Settings(QWidget *p,
      const QString &s, const QString &b) :
      KDialogBase(p,"settings",true,
      i18n("Directories"),
      KDialogBase::Ok|KDialogBase::Cancel, KDialogBase::Ok, true )
{
      QVBox *page = makeVBoxMainWidget();
      QHBox *h = new QHBox(page);
      (void) new QLabel(i18n("Source prefix:"),h);
      edit_src = new KLineEdit(h);
      edit_src->setText(s);

      h = new QHBox(page);
      (void) new QLabel(i18n("Build prefix:"),h);
      edit_bld = new KLineEdit(h);
      edit_bld->setText(b);
}

void PluginKateMakeView::slotConfigure()
{
      Kate::View *kv = win->viewManager()->activeView();


      Settings s(kv,source_prefix,build_prefix);

      if (!s.exec()) return;


      source_prefix = s.edit_src->text();
      build_prefix = s.edit_bld->text();

      if (source_prefix.isEmpty())
      {
            if (!filenameDetector)
            {
                  filenameDetector = new QRegExp(
                        QString::fromLatin1("[a-zA-Z0-9_.-]*\\.[chp]*:[0-9]*:"));
            }
      }
      else
      {
            if (filenameDetector)
            {
                  delete filenameDetector;
                  filenameDetector = 0L;
            }
      }

      KConfig c("katemakepluginrc");
      c.setGroup("Prefixes");
      c.writeEntry("Source",source_prefix);
      c.writeEntry("Build",build_prefix);
}

Generated by  Doxygen 1.6.0   Back to index