/* 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., 51 Franklin Street, Fifth Floor, Boston, ** MA 02110-1301, USA. */ #include "plugin_katemake.moc" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include K_EXPORT_COMPONENT_FACTORY( katemakeplugin, KGenericFactory( "katemake" ) ) // #define FUNCTIONSETUP kdDebug() << k_funcinfo << endl; #define FUNCTIONSETUP PluginKateMake::PluginKateMake( TQObject* parent, const char* name, const TQStringList& ) : Kate::Plugin ( (Kate::Application *)parent, name ) { FUNCTIONSETUP; } PluginKateMake::~PluginKateMake() { FUNCTIONSETUP; } void PluginKateMake::addView(Kate::MainWindow *win) { FUNCTIONSETUP; Kate::ToolViewManager *viewmanager = win->toolViewManager(); TQWidget *w = viewmanager->createToolView("kate_plugin_make", Kate::ToolViewManager::Bottom, SmallIcon(TQString::fromLatin1("misc")), i18n("Make Output")); PluginKateMakeView *view = new PluginKateMakeView (w,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); } 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); } } } #define COL_LINE (1) #define COL_FILE (0) #define COL_MSG (2) class ErrorMessage : public TQListViewItem { public: ErrorMessage(TQListView *parent, const TQString &filename, int lineno, const TQString &message) : TQListViewItem(parent, filename, (lineno > 0 ? TQString::number(lineno) : TQString()), message) { m_isError = !message.contains(TQString::fromLatin1("warning")); m_lineno = lineno; m_serial = s_serial++; } ErrorMessage(TQListView *parent,const TQString &message) : TQListViewItem(parent,TQString(),TQString(),TQString()) { TQString m(message); m.remove('\n'); m.stripWhiteSpace(); setText(COL_MSG,m); m_isError=false; m_lineno=-1; m_serial = s_serial++; setSelectable(false); } ErrorMessage(TQListView *parent, bool start) : TQListViewItem(parent,TQString()) { 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; } TQString message() const { return text(COL_MSG); } TQString fancyMessage() const; TQString caption() const; TQString filename() const { return text(COL_FILE); } int line() const { return m_lineno; } int serial() const { return m_serial; } virtual int compare(TQListViewItem *,int,bool) const; static void resetSerial() { s_serial=10; } protected: virtual void paintCell(TQPainter *,const TQColorGroup &, 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() { } TQString ErrorMessage::caption() const { return TQString::fromLatin1("%1:%2").tqarg(text(COL_FILE)).tqarg(line()); } TQString ErrorMessage::fancyMessage() const { TQString msg = TQString::fromLatin1(""); if (isError()) { msg.append(TQString::fromLatin1("")); } msg.append(message()); if (isError()) { msg.append(TQString::fromLatin1("")); } msg.append(TQString::fromLatin1("")); return msg; } /* virtual */ void ErrorMessage::paintCell(TQPainter *p, const TQColorGroup &cg, int column, int width, int align) { if ((column!=COL_LINE) || (serial()<0)) { TQListViewItem::paintCell(p,cg,column,width,align); } else { TQColorGroup myCG(cg); #if 0 red, //darkRed, green, //darkGreen, blue, //darkBlue, cyan, // darkCyan, magenta, // darkMagenta, yellow, //darkYellow, gray); #endif myCG.setColor(TQColorGroup::Light,red); if (!isSelected()) { myCG.setColor(TQColorGroup::Base,gray); myCG.setColor(TQColorGroup::Text, m_isError ? red : yellow); } TQListViewItem::paintCell(p,myCG,column,width,align); } } /* virtual */ int ErrorMessage::compare(TQListViewItem *i, int /* column */ , bool /* ascending */) const { kdDebug() << "In compare " << serial() << endl; ErrorMessage *e = dynamic_cast(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( TQWidget *parent=0, const char *name=0, WFlags f=0 ); virtual ~LinePopup(); public: static LinePopup *message(TQWidget *, const TQPoint &p,ErrorMessage *e); protected: virtual void positionSelf(); TQPoint fLoc; // There should be only one static LinePopup *one; } ; /* static */ LinePopup *LinePopup::one = 0L; LinePopup::LinePopup(TQWidget *p,const char *n,WFlags f) : KPassivePopup(p,n,f), fLoc(-1,-1) { Q_ASSERT(!one); one=this; } LinePopup::~LinePopup() { one=0L; } /* static */ LinePopup *LinePopup::message(TQWidget *parent, const TQPoint &p, ErrorMessage *e) { if (one) delete one; LinePopup *pop = new LinePopup( parent ); pop->setAutoDelete( true ); pop->setView( e->caption(), e->fancyMessage(), TQPixmap() ); // 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(TQRect(fLoc.x(),fLoc.y(),40,30)); } } PluginKateMakeView::PluginKateMakeView(TQWidget *parent, Kate::MainWindow *mainwin, const char* name) : TQListView(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), TQT_TQOBJECT(this), TQT_SLOT( slotNext() ), actionCollection(), "make_right" ); (void) new KAction ( i18n("Previous Error"), KShortcut(ALT+CTRL+Key_Left), TQT_TQOBJECT(this), TQT_SLOT( slotPrev() ), actionCollection(), "make_left" ); (void) new KAction ( i18n("Make"), KShortcut(ALT+Key_R), TQT_TQOBJECT(this), TQT_SLOT( slotValidate() ), actionCollection(), "make_check" ); (void) new KAction ( i18n("Configure..."), KShortcut(), TQT_TQOBJECT(this), TQT_SLOT( slotConfigure() ), actionCollection(), "make_settings" ); setInstance(new KInstance("kate")); setXMLFile(TQString::fromLatin1("plugins/katemake/ui.rc")); setFocusPolicy(TQ_NoFocus); setSorting(COL_LINE); addColumn(i18n("File"), -1); addColumn(i18n("Line"), -1); setColumnAlignment(COL_LINE, AlignRight); addColumn(i18n("Message"), -1); setAllColumnsShowFocus(true); setResizeMode(TQListView::LastColumn); connect(this, TQT_SIGNAL(clicked(TQListViewItem *)), TQT_SLOT(slotClicked(TQListViewItem *))); m_proc = new KProcess(); connect(m_proc, TQT_SIGNAL(processExited(KProcess*)), this, TQT_SLOT(slotProcExited(KProcess*))); connect(m_proc, TQT_SIGNAL(receivedStderr(KProcess*,char*,int)), TQT_TQOBJECT(this), TQT_SLOT(slotReceivedProcStderr(KProcess*, char*, int))); KConfig c("katemakepluginrc"); c.setGroup("Prefixes"); source_prefix = c.readEntry("Source",TQString()); build_prefix = c.readEntry("Build",TQString()); // if (source_prefix.isEmpty()) { filenameDetector = new TQRegExp( TQString::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 TQString &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(1) #" << e->serial() << endl; return; } int ofs1 = l.find(':'); int ofs2 = l.find(':',ofs1+1); // TQString m = l.mid(ofs2+1); m.remove('\n'); m.stripWhiteSpace(); TQString 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(2) #" << e->serial() << endl; // Should cache files being found and check for // existence. kdDebug() << ": Looking at " << document_dir+filename << endl; if (!TQFile::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; TQString l = TQString::fromLocal8Bit( TQCString(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; TQApplication::restoreOverrideCursor(); sort(); if ( found_error || !p->normalExit() || p->exitStatus() ) { TQListViewItem *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 TQListViewItem *i = new TQListViewItem(this,TQString(), TQString(), i18n("No Errors.")); i->setSelectable(false); #else (void) new ErrorMessage(this,false); #endif } } void PluginKateMakeView::slotClicked(TQListViewItem *item) { FUNCTIONSETUP; if (!item) { kdDebug() << ": No item clicked." << endl; return; } if (!item->isSelectable()) return; ErrorMessage *e = dynamic_cast(item); if (!e) return; ensureItemVisible(e); TQString filename = document_dir + e->filename(); int lineno = e->line(); if (!build_prefix.isEmpty()) { filename = e->filename(); } kdDebug() << ": Looking at " << filename << ":" << lineno << endl; if (TQFile::exists(filename)) { KURL u; u.setPath(filename); win->viewManager()->openURL(u); Kate::View *kv = win->viewManager()->activeView(); kv->setCursorPositionReal(lineno-1,1); TQPoint globalPos = kv->mapToGlobal(kv->cursorCoordinates()); kdDebug() << ": Want to map at " << globalPos.x() << "," << globalPos.y() << endl; #if 0 KPassivePopup::message( TQString::fromLatin1("%1:%2").tqarg(filename).tqarg(lineno), msg, this); #else if ( ! isVisible() ) LinePopup::message(this,globalPos,e); #endif } } void PluginKateMakeView::slotNext() { FUNCTIONSETUP; TQListViewItem *i = selectedItem(); if (!i) return; TQListViewItem *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; TQListViewItem *i = selectedItem(); if (!i) return; TQListViewItem *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 = TQString(); ErrorMessage::resetSerial(); found_error=false; kdDebug() << ": Document " << url.protocol() << " : " << url.path() << endl; if (!url.isLocalFile()) { KMessageBox::sorry(0, i18n("The file %1 is not a local file. " "Non-local files cannot be compiled.") .tqarg(url.path())); return false; } document_dir = TQFileInfo(url.path()).dirPath(true) + TQString::fromLatin1("/"); if (document_dir.startsWith(source_prefix)) { document_dir = build_prefix + document_dir.mid(source_prefix.length()); } m_proc->setWorkingDirectory(document_dir); TQString make = KStandardDirs::findExe( "gmake" ); if (make.isEmpty()) make = KStandardDirs::findExe("make"); *m_proc << make; if( make.isEmpty() || ! m_proc->start(KProcess::NotifyOnExit, KProcess::AllOutput) ) { KMessageBox::error(0, i18n("Error: Failed to run %1.").tqarg(make.isEmpty() ? "make" : make)); return false; } TQApplication::setOverrideCursor(KCursor::waitCursor()); running_indicator = new ErrorMessage(this,true); return true; } class Settings : public KDialogBase { public: Settings( TQWidget *parent, const TQString &src, const TQString &bld); KLineEdit *edit_src,*edit_bld; } ; Settings::Settings(TQWidget *p, const TQString &s, const TQString &b) : KDialogBase(p,"settings",true, i18n("Directories"), KDialogBase::Ok|KDialogBase::Cancel, KDialogBase::Ok, true ) { TQVBox *page = makeVBoxMainWidget(); TQHBox *h = new TQHBox(page); (void) new TQLabel(i18n("Source prefix:"),h); edit_src = new KLineEdit(h); edit_src->setText(s); h = new TQHBox(page); (void) new TQLabel(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 TQRegExp( TQString::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); }