summaryrefslogtreecommitdiffstats
path: root/languages/ruby/debugger
diff options
context:
space:
mode:
authortoma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2009-11-25 17:56:58 +0000
committertoma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2009-11-25 17:56:58 +0000
commit114a878c64ce6f8223cfd22d76a20eb16d177e5e (patch)
treeacaf47eb0fa12142d3896416a69e74cbf5a72242 /languages/ruby/debugger
downloadtdevelop-114a878c64ce6f8223cfd22d76a20eb16d177e5e.tar.gz
tdevelop-114a878c64ce6f8223cfd22d76a20eb16d177e5e.zip
Copy the KDE 3.5 branch to branches/trinity for new KDE 3.5 features.
BUG:215923 git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/kdevelop@1054174 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'languages/ruby/debugger')
-rw-r--r--languages/ruby/debugger/Makefile.am25
-rw-r--r--languages/ruby/debugger/breakpoint.cpp343
-rw-r--r--languages/ruby/debugger/breakpoint.h214
-rw-r--r--languages/ruby/debugger/dbgcommand.cpp47
-rw-r--r--languages/ruby/debugger/dbgcommand.h64
-rw-r--r--languages/ruby/debugger/dbgcontroller.cpp46
-rw-r--r--languages/ruby/debugger/dbgcontroller.h161
-rw-r--r--languages/ruby/debugger/dbgpsdlg.cpp170
-rw-r--r--languages/ruby/debugger/dbgpsdlg.h60
-rw-r--r--languages/ruby/debugger/dbgtoolbar.cpp483
-rw-r--r--languages/ruby/debugger/dbgtoolbar.h90
-rw-r--r--languages/ruby/debugger/debuggee.rb1214
-rw-r--r--languages/ruby/debugger/debuggerpart.cpp785
-rw-r--r--languages/ruby/debugger/debuggerpart.h110
-rw-r--r--languages/ruby/debugger/framestackwidget.cpp272
-rw-r--r--languages/ruby/debugger/framestackwidget.h115
-rw-r--r--languages/ruby/debugger/hi16-action-breakpoint_add.pngbin0 -> 225 bytes
-rw-r--r--languages/ruby/debugger/hi16-action-breakpoint_delete.pngbin0 -> 239 bytes
-rw-r--r--languages/ruby/debugger/hi16-action-breakpoint_delete_all.pngbin0 -> 239 bytes
-rw-r--r--languages/ruby/debugger/hi16-action-breakpoint_edit.pngbin0 -> 277 bytes
-rw-r--r--languages/ruby/debugger/kdevrbdebugger.desktop73
-rw-r--r--languages/ruby/debugger/kdevrbdebugger.rc68
-rw-r--r--languages/ruby/debugger/rdbbreakpointwidget.cpp921
-rw-r--r--languages/ruby/debugger/rdbbreakpointwidget.h120
-rw-r--r--languages/ruby/debugger/rdbcommand.cpp81
-rw-r--r--languages/ruby/debugger/rdbcommand.h99
-rw-r--r--languages/ruby/debugger/rdbcontroller.cpp1414
-rw-r--r--languages/ruby/debugger/rdbcontroller.h192
-rw-r--r--languages/ruby/debugger/rdboutputwidget.cpp171
-rw-r--r--languages/ruby/debugger/rdboutputwidget.h69
-rw-r--r--languages/ruby/debugger/rdbparser.cpp350
-rw-r--r--languages/ruby/debugger/rdbparser.h41
-rw-r--r--languages/ruby/debugger/rdbtable.cpp62
-rw-r--r--languages/ruby/debugger/rdbtable.h45
-rw-r--r--languages/ruby/debugger/stty.cpp370
-rw-r--r--languages/ruby/debugger/stty.h71
-rw-r--r--languages/ruby/debugger/variablewidget.cpp1018
-rw-r--r--languages/ruby/debugger/variablewidget.h348
38 files changed, 9712 insertions, 0 deletions
diff --git a/languages/ruby/debugger/Makefile.am b/languages/ruby/debugger/Makefile.am
new file mode 100644
index 00000000..f4c512c2
--- /dev/null
+++ b/languages/ruby/debugger/Makefile.am
@@ -0,0 +1,25 @@
+# Here resides the debugger part.
+
+INCLUDES = -I$(top_srcdir)/languages/lib/debugger \
+ -I$(top_srcdir)/lib/interfaces -I$(top_srcdir)/lib/interfaces/extensions -I$(top_srcdir)/lib/util \
+ -I$(top_srcdir)/lib/widgets $(all_includes)
+
+kde_module_LTLIBRARIES = libkdevrbdebugger.la
+libkdevrbdebugger_la_LDFLAGS = $(all_libraries) $(KDE_PLUGIN)
+libkdevrbdebugger_la_LIBADD = $(top_builddir)/lib/libkdevelop.la $(top_builddir)/lib/widgets/libkdevwidgets.la $(LIB_KHTML) \
+ $(top_builddir)/languages/lib/debugger/liblang_debugger.la
+
+libkdevrbdebugger_la_SOURCES = debuggerpart.cpp dbgcontroller.cpp rdbcontroller.cpp dbgcommand.cpp rdbcommand.cpp rdbparser.cpp stty.cpp breakpoint.cpp variablewidget.cpp rdbbreakpointwidget.cpp framestackwidget.cpp dbgpsdlg.cpp dbgtoolbar.cpp rdboutputwidget.cpp rdbtable.cpp
+
+METASOURCES = AUTO
+KDE_ICON = AUTO
+
+rubysrc_DATA = debuggee.rb
+rubysrcdir = $(kde_datadir)/kdevrbdebugger
+
+servicedir = $(kde_servicesdir)
+service_DATA = kdevrbdebugger.desktop
+
+rcdir = $(kde_datadir)/kdevrbdebugger
+rc_DATA = kdevrbdebugger.rc
+noinst_HEADERS = rdbtable.h
diff --git a/languages/ruby/debugger/breakpoint.cpp b/languages/ruby/debugger/breakpoint.cpp
new file mode 100644
index 00000000..f210e359
--- /dev/null
+++ b/languages/ruby/debugger/breakpoint.cpp
@@ -0,0 +1,343 @@
+/***************************************************************************
+ begin : Tue May 13 2003
+ copyright : (C) 2003 by John Birch
+ email : jbb@kdevelop.org
+
+ Adapted for ruby debugging
+ --------------------------
+ begin : Mon Nov 1 2004
+ copyright : (C) 2004 by Richard Dale
+ email : Richard_Dale@tipitina.demon.co.uk
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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 "breakpoint.h"
+
+#include <klocale.h>
+
+#include <qfileinfo.h>
+#include <qfontmetrics.h>
+#include <qpainter.h>
+#include <qregexp.h>
+#include <qstring.h>
+
+#include <stdio.h>
+
+/***************************************************************************/
+/***************************************************************************/
+/***************************************************************************/
+
+namespace RDBDebugger
+{
+
+static int BPKey_ = 0;
+
+/***************************************************************************/
+/***************************************************************************/
+/***************************************************************************/
+
+Breakpoint::Breakpoint(bool temporary, bool enabled)
+ : s_pending_(true),
+ s_actionAdd_(true),
+ s_actionClear_(false),
+ s_actionModify_(false),
+ s_actionDie_(false),
+ s_dbgProcessing_(false),
+ s_enabled_(enabled),
+ s_temporary_(temporary),
+ s_changedEnable_(false),
+ key_(BPKey_++),
+ active_(-1)
+{
+}
+
+/***************************************************************************/
+
+Breakpoint::~Breakpoint()
+{
+}
+
+/***************************************************************************/
+
+QString Breakpoint::dbgRemoveCommand() const
+{
+// if (dbgId_>0)
+// return QString("delete %1").arg(dbgId_); // gdb command - not translatable
+
+ return QString();
+}
+
+/***************************************************************************/
+
+// called when debugger ends
+void Breakpoint::reset()
+{
+ dbgId_ = -1;
+ s_pending_ = true;
+ s_actionAdd_ = true; // waiting for the debugger to start
+ s_actionClear_ = false;
+ s_changedEnable_ = !s_enabled_;
+ s_actionModify_ = s_changedEnable_;
+ s_dbgProcessing_ = false;
+// hits_ = 0;
+ active_ = -1;
+}
+
+/***************************************************************************/
+
+void Breakpoint::setActive(int active, int id)
+{
+ active_ = active;
+ dbgId_ = id;
+
+ if (s_pending_ && !(s_actionAdd_ && s_actionModify_)) {
+ s_pending_ = false;
+ s_actionModify_ = false;
+ }
+
+ s_actionAdd_ = false;
+ s_actionClear_ = false;
+ s_actionDie_ = false;
+ s_dbgProcessing_ = false;
+
+ if (!s_actionModify_) {
+ s_changedEnable_ = false;
+ }
+}
+
+/***************************************************************************/
+
+QString Breakpoint::statusDisplay(int activeFlag) const
+{
+ QString status="";
+ if (!s_enabled_)
+ status = i18n("Disabled");
+ else
+ if (s_pending_)
+ {
+ if (s_actionAdd_)
+ status = i18n("Pending (add)");
+ if (s_actionClear_)
+ status = i18n("Pending (clear)");
+ if (s_actionModify_)
+ status = i18n("Pending (modify)");
+ }
+ else
+ if (isActive(activeFlag))
+ status = i18n("Active");
+
+ return status;
+}
+
+/***************************************************************************/
+/***************************************************************************/
+/***************************************************************************/
+
+FilePosBreakpoint::FilePosBreakpoint(const QString &fileName, int lineNum,
+ bool temporary, bool enabled)
+ : Breakpoint(temporary, enabled),
+ fileName_(fileName),
+ lineNo_(lineNum)
+{
+}
+
+/***************************************************************************/
+
+FilePosBreakpoint::~FilePosBreakpoint()
+{
+}
+
+/***************************************************************************/
+
+QString FilePosBreakpoint::dbgSetCommand() const
+{
+ QString cmdStr;
+ if (fileName_.isEmpty())
+ cmdStr = QString("break %1").arg(lineNo_); // gdb command - not translatable
+ else {
+ cmdStr = QString("break %1:%2").arg(fileName_).arg(lineNo_);
+ }
+
+ if (isTemporary())
+ cmdStr = "t"+cmdStr; // gdb command
+
+ return cmdStr;
+}
+
+/***************************************************************************/
+
+bool FilePosBreakpoint::match(const Breakpoint *brkpt) const
+{
+ // simple case
+ if (this == brkpt)
+ return true;
+
+ // Type case
+ const FilePosBreakpoint* check = dynamic_cast<const FilePosBreakpoint*>(brkpt);
+ if (!check)
+ return false;
+
+ // member case
+ return ( (fileName_ == check->fileName_) &&
+ (lineNo_ == check->lineNo_));
+}
+
+/***************************************************************************/
+
+QString FilePosBreakpoint::location(bool compact)
+{
+ if (compact)
+ return QFileInfo(fileName_).fileName()+":"+QString::number(lineNo_);
+
+ return fileName_+":"+QString::number(lineNo_);
+}
+
+/***************************************************************************/
+
+void FilePosBreakpoint::setLocation(const QString& location)
+{
+ QRegExp regExp1("(.*):(\\d+)$");
+ regExp1.setMinimal(true);
+ if ( regExp1.search(location, 0) >= 0 )
+ {
+ QString t = regExp1.cap(1);
+ QString dirPath = QFileInfo(t).dirPath();
+ if ( dirPath == "." )
+ fileName_ = QFileInfo(fileName_).dirPath()+"/"+regExp1.cap(1);
+ else
+ fileName_ = regExp1.cap(1);
+
+ lineNo_ = regExp1.cap(2).toInt();
+ }
+}
+
+/***************************************************************************/
+/***************************************************************************/
+/***************************************************************************/
+
+Watchpoint::Watchpoint(const QString& varName, bool temporary, bool enabled)
+ : Breakpoint(temporary, enabled),
+ varName_(varName)
+{
+}
+
+/***************************************************************************/
+
+Watchpoint::~Watchpoint()
+{
+}
+
+/***************************************************************************/
+
+QString Watchpoint::dbgSetCommand() const
+{
+ return QString("watch ")+varName_; // gdb command - not translatable
+}
+
+/***************************************************************************/
+
+bool Watchpoint::match(const Breakpoint* brkpt) const
+{
+ // simple case
+ if (this == brkpt)
+ return true;
+
+ // Type case
+ const Watchpoint *watch = dynamic_cast<const Watchpoint*>(brkpt);
+ if (watch == 0)
+ return false;
+
+ // member case
+ return (varName_ == watch->varName_);
+}
+
+/***************************************************************************/
+/***************************************************************************/
+/***************************************************************************/
+
+Catchpoint::Catchpoint(const QString& varName, bool temporary, bool enabled)
+ : Breakpoint(temporary, enabled),
+ varName_(varName)
+{
+}
+
+/***************************************************************************/
+
+Catchpoint::~Catchpoint()
+{
+}
+
+/***************************************************************************/
+
+QString Catchpoint::dbgSetCommand() const
+{
+ return QString("catch ")+varName_; // gdb command - not translatable
+}
+
+/***************************************************************************/
+
+bool Catchpoint::match(const Breakpoint* brkpt) const
+{
+ // simple case
+ if (this == brkpt)
+ return true;
+
+ // Type case
+ const Catchpoint *check = dynamic_cast<const Catchpoint*>(brkpt);
+ if (check == 0)
+ return false;
+
+ // member case
+ return (varName_ == check->varName_);
+}
+
+/***************************************************************************/
+/***************************************************************************/
+/***************************************************************************/
+
+FunctionBreakpoint::FunctionBreakpoint(const QString& functionName, bool temporary, bool enabled)
+ : Breakpoint(temporary, enabled),
+ m_functionName(functionName)
+{
+}
+
+/***************************************************************************/
+
+FunctionBreakpoint::~FunctionBreakpoint()
+{
+}
+
+/***************************************************************************/
+
+QString FunctionBreakpoint::dbgSetCommand() const
+{
+ return QString("break ")+m_functionName; // gdb command - not translatable
+}
+
+/***************************************************************************/
+
+bool FunctionBreakpoint::match(const Breakpoint* brkpt) const
+{
+ // simple case
+ if (this == brkpt)
+ return true;
+
+ // Type case
+ const FunctionBreakpoint *check = dynamic_cast<const FunctionBreakpoint*>(brkpt);
+ if (!check)
+ return false;
+
+ // member case
+ return (m_functionName == check->m_functionName);
+}
+
+
+}
diff --git a/languages/ruby/debugger/breakpoint.h b/languages/ruby/debugger/breakpoint.h
new file mode 100644
index 00000000..e45e3856
--- /dev/null
+++ b/languages/ruby/debugger/breakpoint.h
@@ -0,0 +1,214 @@
+/***************************************************************************
+ begin : Tue May 13 2003
+ copyright : (C) 2003 by John Birch
+ email : jbb@kdevelop.org
+
+ Adapted for ruby debugging
+ --------------------------
+ begin : Mon Nov 1 2004
+ copyright : (C) 2004 by Richard Dale
+ email : Richard_Dale@tipitina.demon.co.uk
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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. *
+ * *
+ ***************************************************************************/
+
+#ifndef _BREAKPOINT_H_
+#define _BREAKPOINT_H_
+
+#include <klocale.h>
+
+#include <qstring.h>
+
+/***************************************************************************/
+/***************************************************************************/
+/***************************************************************************/
+
+namespace RDBDebugger
+{
+
+enum BP_TYPES
+{
+ BP_TYPE_Invalid,
+ BP_TYPE_FilePos,
+ BP_TYPE_Watchpoint,
+ BP_TYPE_Catchpoint,
+ BP_TYPE_Function
+};
+
+class Breakpoint
+{
+public:
+ Breakpoint(bool temporary=false, bool enabled=true);
+ virtual ~Breakpoint();
+
+ virtual QString dbgSetCommand() const = 0;
+ virtual QString dbgRemoveCommand() const;
+ virtual bool match(const Breakpoint* brkpt) const = 0;
+ virtual void reset();
+
+ void setActive(int active, int id);
+ bool isActive(int active) const { return (active_ == active) ||
+ (s_pending_ && !s_actionClear_); }
+ void setEnabled(bool enabled) { s_changedEnable_ = (s_enabled_ != enabled);
+ s_enabled_ = enabled; }
+ bool isEnabled() const { return s_enabled_; }
+ void setTemporary(bool temporary) { s_temporary_ = temporary; }
+ bool isTemporary() const { return s_temporary_; }
+
+ bool changedEnable() const { return s_changedEnable_; }
+
+ void setPending(bool pending) { s_pending_ = pending; }
+ bool isPending() const { return s_pending_; }
+ void setActionAdd(bool actionAdd) { s_actionDie_ = false;
+ s_actionAdd_ = actionAdd; }
+ bool isActionAdd() const { return s_actionAdd_; }
+ void setActionClear(bool actionClear) { s_actionClear_ = actionClear; }
+ bool isActionClear() const { return s_actionClear_; }
+ void setActionModify(bool actionModify) { s_actionDie_ = false;
+ s_actionModify_ = actionModify; }
+ bool isActionModify() const { return s_actionModify_; }
+ void setDbgProcessing(bool dbgProcessing) { s_dbgProcessing_ = dbgProcessing; }
+ bool isDbgProcessing() const { return s_dbgProcessing_; }
+ void setActionDie() { s_actionDie_ = true;
+ s_actionClear_ = false; }
+ bool isActionDie() const { return s_actionDie_; }
+
+ int key() const { return key_; }
+ void setDbgId(int dbgId) { dbgId_ = dbgId; }
+ int dbgId() const { return dbgId_; }
+
+ virtual QString statusDisplay(int activeFlag) const;
+ virtual BP_TYPES type() const { return BP_TYPE_Invalid; }
+ virtual QString displayType() const { return i18n( "Invalid" ); }
+
+ virtual QString location(bool compact=true) = 0;
+ virtual void setLocation(const QString& ) = 0;
+ virtual bool isValid() const = 0;
+
+private:
+ bool s_pending_ :1;
+ bool s_actionAdd_ :1;
+ bool s_actionClear_ :1;
+ bool s_actionModify_ :1;
+ bool s_actionDie_ :1;
+ bool s_dbgProcessing_ :1;
+ bool s_enabled_ :1;
+ bool s_temporary_ :1;
+ bool s_changedEnable_ :1;
+
+ int dbgId_; // assigned by gdb
+
+ int key_; // internal unique key
+ int active_; // counter incremented on receipt of all BP's
+
+ int ignoreCount_;
+ QString condition_;
+// QString type_;
+};
+
+/***************************************************************************/
+/***************************************************************************/
+/***************************************************************************/
+class FilePosBreakpoint : public Breakpoint
+{
+public:
+ FilePosBreakpoint(const QString &fileName, int lineNum,
+ bool temporary=false, bool enabled=true);
+ virtual ~FilePosBreakpoint();
+ virtual QString dbgSetCommand() const;
+ virtual bool match(const Breakpoint *brkpt) const;
+
+ BP_TYPES type () const { return BP_TYPE_FilePos; }
+ QString displayType() const { return i18n( "File:line" ); }
+ void setFileName(const QString& fileName) { fileName_ = fileName; }
+ QString fileName() const { return fileName_; }
+ void setLineNum(int lineNum) { lineNo_ = lineNum; }
+ int lineNum() const { return lineNo_; }
+ QString location(bool compact=true);
+ void setLocation(const QString& location);
+ bool isValid() const { return lineNo_>0 && !fileName_.isEmpty(); }
+
+private:
+ QString fileName_;
+ int lineNo_;
+};
+
+/***************************************************************************/
+/***************************************************************************/
+/***************************************************************************/
+class Watchpoint : public Breakpoint
+{
+public:
+ Watchpoint(const QString &varName, bool temporary=false, bool enabled=true);
+ virtual ~Watchpoint();
+ virtual QString dbgSetCommand() const;
+ bool match(const Breakpoint *brkpt) const;
+
+ BP_TYPES type () const { return BP_TYPE_Watchpoint; }
+ QString displayType() const { return i18n("Watchpoint"); }
+ void setVarName(const QString& varName) { varName_ = varName; }
+ QString varName() const { return varName_; }
+ QString location(bool) { return varName_; }
+ void setLocation(const QString& location) { varName_ = location; }
+ bool isValid() const { return !varName_.isEmpty(); }
+
+private:
+ QString varName_;
+};
+
+/***************************************************************************/
+/***************************************************************************/
+/***************************************************************************/
+class Catchpoint : public Breakpoint
+{
+public:
+ Catchpoint(const QString &varName, bool temporary=false, bool enabled=true);
+ virtual ~Catchpoint();
+ virtual QString dbgSetCommand() const;
+ bool match(const Breakpoint *brkpt) const;
+
+ BP_TYPES type () const { return BP_TYPE_Catchpoint; }
+ QString displayType() const { return i18n("Catchpoint"); }
+ void setVarName(const QString& varName) { varName_ = varName; }
+ QString varName() const { return varName_; }
+ QString location(bool) { return varName_; }
+ void setLocation(const QString& location) { varName_ = location; }
+ bool isValid() const { return !varName_.isEmpty(); }
+
+private:
+ QString varName_;
+};
+
+/***************************************************************************/
+/***************************************************************************/
+/***************************************************************************/
+class FunctionBreakpoint : public Breakpoint
+{
+public:
+ FunctionBreakpoint(const QString &functionName, bool temporary=false, bool enabled=true);
+ virtual ~FunctionBreakpoint();
+ virtual QString dbgSetCommand() const;
+ bool match(const Breakpoint *brkpt) const;
+
+ BP_TYPES type () const { return BP_TYPE_Function; }
+ QString displayType() const { return i18n("Method()"); }
+ void setfunctionName(const QString& functionName) { m_functionName = functionName; }
+ QString functionName() const { return m_functionName; }
+ QString location(bool) { return m_functionName; };
+ void setLocation(const QString& location) { m_functionName = location; }
+ bool isValid() const { return !m_functionName.isEmpty(); }
+
+private:
+ QString m_functionName;
+};
+
+}
+
+#endif
diff --git a/languages/ruby/debugger/dbgcommand.cpp b/languages/ruby/debugger/dbgcommand.cpp
new file mode 100644
index 00000000..6c6a24ec
--- /dev/null
+++ b/languages/ruby/debugger/dbgcommand.cpp
@@ -0,0 +1,47 @@
+/***************************************************************************
+ begin : Sun Aug 8 1999
+ copyright : (C) 1999 by John Birch
+ email : jbb@kdevelop.org
+
+ Adapted for ruby debugging
+ --------------------------
+ begin : Mon Nov 1 2004
+ copyright : (C) 2004 by Richard Dale
+ email : Richard_Dale@tipitina.demon.co.uk
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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 "dbgcommand.h"
+
+#include <qstring.h>
+
+/***************************************************************************/
+/***************************************************************************/
+/***************************************************************************/
+namespace RDBDebugger
+{
+
+
+DbgCommand::DbgCommand(const QCString& command, bool isRunCmd, bool isInfoCmd) :
+ command_(command),
+ isRunCmd_(isRunCmd),
+ isInfoCmd_(isInfoCmd),
+ sent_(false),
+ waitForReply_(true)
+{
+ cmdBuffer_ = command_+"\n";
+}
+
+}
+
+/***************************************************************************/
+/***************************************************************************/
+/***************************************************************************/
diff --git a/languages/ruby/debugger/dbgcommand.h b/languages/ruby/debugger/dbgcommand.h
new file mode 100644
index 00000000..32ec7f7a
--- /dev/null
+++ b/languages/ruby/debugger/dbgcommand.h
@@ -0,0 +1,64 @@
+/***************************************************************************
+ begin : Sun Aug 8 1999
+ copyright : (C) 1999 by John Birch
+ email : jbb@kdevelop.org
+
+ Adapted for ruby debugging
+ --------------------------
+ begin : Mon Nov 1 2004
+ copyright : (C) 2004 by Richard Dale
+ email : Richard_Dale@tipitina.demon.co.uk
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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. *
+ * *
+ ***************************************************************************/
+
+#ifndef DBGCOMMAND_H
+#define DBGCOMMAND_H
+
+#include <qstring.h>
+
+/**
+ * @author John Birch
+ */
+
+namespace RDBDebugger
+{
+
+class DbgCommand
+{
+public:
+ DbgCommand(const QCString& command, bool isRunCmd, bool isInfoCmd);
+ virtual ~DbgCommand() {};
+
+ virtual QCString& cmdToSend() { sent_ = true; return cmdBuffer_; }
+ virtual int cmdLength() { return cmdBuffer_.length(); }
+
+ QCString rawDbgCommand() const { return command_; }
+ bool isARunCmd() const { return isRunCmd_;}
+ bool isAnInfoCmd() const { return isInfoCmd_; }
+ bool moreToSend() const { return !sent_; }
+ bool expectReply() const { return waitForReply_; }
+
+protected:
+ QCString cmdBuffer_;
+ QCString command_;
+ bool isRunCmd_;
+ bool isInfoCmd_;
+ bool sent_;
+ bool waitForReply_;
+};
+
+}
+
+/***************************************************************************/
+/***************************************************************************/
+/***************************************************************************/
+
+#endif
diff --git a/languages/ruby/debugger/dbgcontroller.cpp b/languages/ruby/debugger/dbgcontroller.cpp
new file mode 100644
index 00000000..7ee560b7
--- /dev/null
+++ b/languages/ruby/debugger/dbgcontroller.cpp
@@ -0,0 +1,46 @@
+/***************************************************************************
+ begin : Sun Aug 8 1999
+ copyright : (C) 1999 by John Birch
+ email : jbb@kdevelop.org
+
+ Adapted for ruby debugging
+ --------------------------
+ begin : Mon Nov 1 2004
+ copyright : (C) 2004 by Richard Dale
+ email : Richard_Dale@tipitina.demon.co.uk
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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 "dbgcontroller.h"
+#include <kprocess.h>
+
+/***************************************************************************/
+
+namespace RDBDebugger
+{
+
+DbgController::DbgController()
+ : dbgProcess_(0)
+{
+}
+
+/***************************************************************************/
+
+DbgController::~DbgController()
+{
+ delete dbgProcess_;
+}
+
+/***************************************************************************/
+
+}
+
+#include "dbgcontroller.moc"
diff --git a/languages/ruby/debugger/dbgcontroller.h b/languages/ruby/debugger/dbgcontroller.h
new file mode 100644
index 00000000..13ee4382
--- /dev/null
+++ b/languages/ruby/debugger/dbgcontroller.h
@@ -0,0 +1,161 @@
+/***************************************************************************
+ begin : Sun Aug 8 1999
+ copyright : (C) 1999 by John Birch
+ email : jbb@kdevelop.org
+
+ Adapted for ruby debugging
+ --------------------------
+ begin : Mon Nov 1 2004
+ copyright : (C) 2004 by Richard Dale
+ email : Richard_Dale@tipitina.demon.co.uk
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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. *
+ * *
+ ***************************************************************************/
+
+#ifndef _DBGCONTROLLER_H_
+#define _DBGCONTROLLER_H_
+
+#include <qobject.h>
+#include <domutil.h>
+
+class KProcess;
+class QString;
+class QStrList;
+
+namespace RDBDebugger
+{
+
+class Breakpoint;
+class DbgCommand;
+class LazyFetchItem;
+class VarItem;
+
+/***************************************************************************/
+/**
+ * @author jbb
+ */
+/***************************************************************************/
+// sigh - namespace's don't work on some of the older compilers
+enum DBGStateFlags
+{
+ s_dbgNotStarted = 1 << 0,
+ s_appNotStarted = 1 << 1,
+ s_appBusy = 1 << 2,
+ s_waitForWrite = 1 << 3,
+ s_programExited = 1 << 4,
+ s_silent = 1 << 5,
+ s_fetchLocals = 1 << 6,
+ s_viewBT = 1 << 7,
+ s_viewBP = 1 << 8,
+ s_attached = 1 << 9,
+ s_fetchGlobals = 1 << 10,
+ s_waitTimer = 1 << 11,
+ s_shuttingDown = 1 << 12,
+ s_viewThreads = 1 << 13
+};
+
+
+enum RttiValues {
+ RTTI_WATCH_ROOT = 1001,
+ RTTI_GLOBAL_ROOT = 1002,
+ RTTI_VAR_FRAME_ROOT = 1003,
+ RTTI_LAZY_FETCH_ITEM = 1004,
+ RTTI_VAR_ITEM = 1005,
+ RTTI_WATCH_VAR_ITEM = 1006,
+ RTTI_THREAD_STACK_ITEM = 1007,
+ RTTI_FRAME_STACK_ITEM = 1008
+};
+
+
+/***************************************************************************/
+/***************************************************************************/
+/***************************************************************************/
+class DbgController : public QObject
+{
+ Q_OBJECT
+
+public:
+
+ DbgController();
+ virtual ~DbgController();
+
+ virtual bool stateIsOn( int state ) = 0;
+
+protected:
+ virtual void queueCmd(DbgCommand *cmd, bool executeNext) = 0;
+ virtual void parse(char *str) = 0;
+
+public slots:
+ virtual void configure() = 0;
+
+ /**
+ * Start the debugger
+ * \param ruby_interpreter shell
+ * \param character_coding -K option
+ * \param run_directory Directory from where the program should be run
+ * \param debuggee_path Absolute path to debuggee.rb debugger script
+ * \param application Absolute path to application
+ * \param run_arguments Command line arguments to be passed to the application
+ * \param show_constants Show ruby constants in the variables view
+ */
+ virtual void slotStart(const QString& ruby_interpreter,
+ const QString& character_coding,
+ const QString& run_directory,
+ const QString& debuggee_path,
+ const QString& application,
+ const QString& run_arguments,
+ bool show_constants,
+ bool trace_into_ruby) = 0;
+
+ virtual void slotStopDebugger() = 0;
+
+ virtual void slotRun() = 0;
+ virtual void slotRunUntil(const QString &fileName, int lineNum) = 0;
+ virtual void slotStepInto() = 0;
+ virtual void slotStepOver() = 0;
+ virtual void slotStepOutOff() = 0;
+
+ virtual void slotBreakInto() = 0;
+ virtual void slotBPState(const Breakpoint&) = 0;
+
+
+ virtual void slotExpandItem(VarItem *parent,
+ const QCString &userRequest) = 0;
+ virtual void slotSelectFrame(int frame, int thread,
+ const QString& frameName) = 0;
+ virtual void slotFetchGlobals(bool fetch) = 0;
+
+protected slots:
+ virtual void slotDbgStdout(KProcess *proc, char *buf, int buflen) = 0;
+ virtual void slotDbgStderr(KProcess*, char*, int) {} ;
+ virtual void slotDbgWroteStdin(KProcess *proc) = 0;
+ virtual void slotDbgProcessExited(KProcess *proc) = 0;
+
+ virtual void slotAcceptConnection(int passive_socket) = 0;
+ virtual void slotReadFromSocket(int socket) = 0;
+
+signals:
+ void gotoSourcePosition (const QString &fileName, int lineNum);
+ void rawRDBBreakpointList (char *buf);
+ void rawRDBBreakpointSet (char *buf, int key);
+ void ttyStdout (const char *output);
+ void ttyStderr (const char *output);
+ void rdbStdout (const char *output);
+ void rdbStderr (const char *output);
+ void showStepInSource (const QString &fileName, int lineNum, const QString &address);
+ void dbgStatus (const QString &status, int statusFlag);
+
+protected:
+ KProcess *dbgProcess_;
+};
+
+}
+
+#endif
diff --git a/languages/ruby/debugger/dbgpsdlg.cpp b/languages/ruby/debugger/dbgpsdlg.cpp
new file mode 100644
index 00000000..83dd1666
--- /dev/null
+++ b/languages/ruby/debugger/dbgpsdlg.cpp
@@ -0,0 +1,170 @@
+/***************************************************************************
+ begin : Mon Sep 20 1999
+ copyright : (C) 1999 by John Birch
+ email : jbb@kdevelop.org
+
+ Adapted for ruby debugging
+ --------------------------
+ begin : Mon Nov 1 2004
+ copyright : (C) 2004 by Richard Dale
+ email : Richard_Dale@tipitina.demon.co.uk
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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 "dbgpsdlg.h"
+
+#include <kbuttonbox.h>
+#include <kdialog.h>
+#include <kglobalsettings.h>
+#include <klocale.h>
+#include <kprocess.h>
+#include <kstdguiitem.h>
+#include <kdeversion.h>
+
+#include <qframe.h>
+#include <qlabel.h>
+#include <qlayout.h>
+#include <qlistbox.h>
+#include <qtoolbutton.h>
+#include <qpushbutton.h>
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h>
+
+namespace RDBDebugger
+{
+
+/***************************************************************************/
+
+// Display a list of processes for the user to select one
+// only display processes that they can do something with so if the user
+// is root then display all processes
+// For use with the internal debugger, but this dialog doesn't know anything
+// about why it's doing it.
+
+Dbg_PS_Dialog::Dbg_PS_Dialog(QWidget *parent, const char *name)
+ : KDialog(parent, name, true), // modal
+ psProc_(0),
+ pids_(new QListBox(this)),
+ heading_(new QLabel(" ", this)),
+ pidLines_(QString())
+{
+ setCaption(i18n("Attach to Process"));
+
+ QBoxLayout *topLayout = new QVBoxLayout(this, 5);
+
+ heading_->setFont(KGlobalSettings::fixedFont());
+ heading_->setFrameStyle(QFrame::Panel|QFrame::Sunken);
+ heading_->setMaximumHeight(heading_->sizeHint().height());
+// heading_->setMinimumSize(heading_->sizeHint());
+ topLayout->addWidget(heading_, 5);
+
+ topLayout->addWidget(pids_, 5);
+ pids_->setFont(KGlobalSettings::fixedFont());
+
+ KButtonBox *buttonbox = new KButtonBox(this, Qt::Horizontal, 5);
+ QPushButton *ok = buttonbox->addButton(KStdGuiItem::ok());
+ buttonbox->addStretch();
+ QPushButton *cancel = buttonbox->addButton(KStdGuiItem::cancel());
+ buttonbox->layout();
+ topLayout->addWidget(buttonbox);
+
+ connect(ok, SIGNAL(clicked()), SLOT(accept()));
+ connect(cancel, SIGNAL(clicked()), SLOT(reject()));
+
+ psProc_ = new KShellProcess("/bin/sh");
+ #ifdef USE_SOLARIS
+ *psProc_ << "ps";
+ *psProc_ << "-opid";
+ *psProc_ << "-otty";
+ *psProc_ << "-os";
+ *psProc_ << "-otime";
+ *psProc_ << "-oargs";
+ pidCmd_ = "ps -opid -otty -os -otime -oargs";
+
+ if (getuid() == 0) {
+ *psProc_ << "-e";
+ pidCmd_ += " -e";
+ }
+ #else
+ *psProc_ << "ps";
+ *psProc_ << "x";
+ pidCmd_ = "ps x";
+
+ if (getuid() == 0) {
+ *psProc_ << "a";
+ pidCmd_ += " a";
+ }
+ #endif
+
+ connect( psProc_, SIGNAL(processExited(KProcess *)), SLOT(slotProcessExited()) );
+ connect( psProc_, SIGNAL(receivedStdout(KProcess *, char *, int)), SLOT(slotReceivedOutput(KProcess *, char *, int)) );
+ psProc_->start(KProcess::NotifyOnExit, KProcess::Stdout);
+
+ // Default display to 40 chars wide, default height is okay
+ resize( ((KGlobalSettings::fixedFont()).pointSize())*40, height());
+ topLayout->activate();
+}
+
+/***************************************************************************/
+
+Dbg_PS_Dialog::~Dbg_PS_Dialog()
+{
+ delete psProc_;
+}
+
+/***************************************************************************/
+
+int Dbg_PS_Dialog::pidSelected()
+{
+ QString pidText = pids_->text(pids_->currentItem());
+ if (!pidText.isEmpty())
+ return atoi(pidText.latin1());
+
+ return 0;
+}
+
+/***************************************************************************/
+
+void Dbg_PS_Dialog::slotReceivedOutput(KProcess */*proc*/, char *buffer, int buflen)
+{
+ pidLines_ += QString::fromLocal8Bit(buffer, buflen+1);
+}
+
+/***************************************************************************/
+
+void Dbg_PS_Dialog::slotProcessExited()
+{
+ delete psProc_;
+ psProc_ = 0;
+
+ pidLines_ += '\n';
+
+ int start = pidLines_.find('\n', 0); // Skip the first line (header line)
+ int pos;
+ if (start != -1)
+ heading_->setText(pidLines_.left(start));
+ while ( (pos = pidLines_.find('\n', start)) != -1) {
+ QString item = pidLines_.mid(start, pos-start);
+ if (!item.isEmpty()) {
+ if (item.find(pidCmd_) == -1)
+ pids_->insertItem(item);
+ }
+
+ start = pos+1;
+ }
+}
+
+}
+
+/***************************************************************************/
+#include "dbgpsdlg.moc"
diff --git a/languages/ruby/debugger/dbgpsdlg.h b/languages/ruby/debugger/dbgpsdlg.h
new file mode 100644
index 00000000..641f7aa5
--- /dev/null
+++ b/languages/ruby/debugger/dbgpsdlg.h
@@ -0,0 +1,60 @@
+/***************************************************************************
+ begin : Mon Sep 20 1999
+ copyright : (C) 1999 by John Birch
+ email : jbb@kdevelop.org
+
+ Adapted for ruby debugging
+ --------------------------
+ begin : Mon Nov 1 2004
+ copyright : (C) 2004 by Richard Dale
+ email : Richard_Dale@tipitina.demon.co.uk
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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. *
+ * *
+ ***************************************************************************/
+
+#ifndef _DBGPSDLG_H_
+#define _DBGPSDLG_H_
+
+#include <kdialog.h>
+
+class QListBox;
+class KProcess;
+class QLabel;
+
+namespace RDBDebugger
+{
+
+/***************************************************************************/
+
+class Dbg_PS_Dialog : public KDialog
+{
+ Q_OBJECT
+
+public:
+ Dbg_PS_Dialog( QWidget *parent=0, const char *name=0 );
+ ~Dbg_PS_Dialog();
+
+ int pidSelected();
+
+private slots:
+ void slotReceivedOutput(KProcess *proc, char *buffer, int buflen);
+ void slotProcessExited();
+
+private:
+ KProcess* psProc_;
+ QListBox* pids_;
+ QLabel* heading_;
+ QString pidLines_;
+ QString pidCmd_;
+};
+
+}
+
+#endif
diff --git a/languages/ruby/debugger/dbgtoolbar.cpp b/languages/ruby/debugger/dbgtoolbar.cpp
new file mode 100644
index 00000000..702c8e83
--- /dev/null
+++ b/languages/ruby/debugger/dbgtoolbar.cpp
@@ -0,0 +1,483 @@
+/***************************************************************************
+ begin : Thu Dec 23 1999
+ copyright : (C) 1999 by John Birch
+ email : jbb@kdevelop.org
+
+ Adapted for ruby debugging
+ --------------------------
+ begin : Mon Nov 1 2004
+ copyright : (C) 2004 by Richard Dale
+ email : Richard_Dale@tipitina.demon.co.uk
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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 "dbgtoolbar.h"
+#include "debuggerpart.h"
+#include "dbgcontroller.h"
+
+#include <kdockwindow.h>
+#include <kiconloader.h>
+#include <klocale.h>
+#include <kpopupmenu.h>
+#include <kstandarddirs.h>
+#include <kwin.h>
+#include <kwinmodule.h>
+
+#include <qapplication.h>
+#include <qcursor.h>
+#include <qframe.h>
+#include <qlayout.h>
+#include <qpainter.h>
+#include <qpushbutton.h>
+#include <qtooltip.h>
+#include <qwhatsthis.h>
+
+// **************************************************************************
+// **************************************************************************
+// **************************************************************************
+
+// Implements a floating toolbar for the debugger.
+
+// Unfortunately, I couldn't get the KToolBar to work nicely when it
+// was floating, so I was forced to write these classes. I'm not sure whether
+// I didn't try hard enough or ... and I've forgotten what the problems were
+// now.
+
+// The problem with using this is that it will not dock as a normal toolbar.
+// I'm not convince that this is a real problem though.
+
+// So, if you can get it to work as a KToolBar, and it works well when the
+// app is running, then all these classes can be removed.
+
+// This code is very specific to the internal debugger in kdevelop.
+
+namespace RDBDebugger
+{
+
+// **************************************************************************
+// **************************************************************************
+// **************************************************************************
+
+// This just allows the user to click on the toolbar and drag it somewhere else.
+// I would have preferred to use normal decoration on the toolbar and removed
+// the iconify, close, etc buttons from the window title but again I kept running
+// into problems. Instead, I used no decoration and this class. Also this looks
+// similar to the KToolBar floating style.
+class DbgMoveHandle : public QFrame
+{
+public:
+ DbgMoveHandle(DbgToolBar *parent=0, const char * name=0, WFlags f=0);
+ virtual ~DbgMoveHandle();
+
+ virtual void mousePressEvent(QMouseEvent *e);
+ virtual void mouseReleaseEvent(QMouseEvent *e);
+ virtual void mouseMoveEvent(QMouseEvent *e);
+
+private:
+ DbgToolBar* toolBar_;
+ QPoint offset_;
+ bool moving_;
+};
+
+// **************************************************************************
+
+DbgMoveHandle::DbgMoveHandle(DbgToolBar *parent, const char * name, WFlags f)
+ : QFrame(parent, name, f),
+ toolBar_(parent),
+ offset_(QPoint(0,0)),
+ moving_(false)
+{
+ setFrameStyle(QFrame::Panel|QFrame::Raised);
+ setFixedHeight(12);
+}
+
+// **************************************************************************
+
+DbgMoveHandle::~DbgMoveHandle()
+{
+}
+
+// **************************************************************************
+
+void DbgMoveHandle::mousePressEvent(QMouseEvent *e)
+{
+ QFrame::mousePressEvent(e);
+ if (moving_)
+ return;
+
+ if (e->button() == RightButton) {
+ KPopupMenu *menu = new KPopupMenu(this);
+ menu->insertTitle(i18n("Debug Toolbar"));
+ menu->insertItem(i18n("Dock to Panel"),
+ parent(), SLOT(slotDock()));
+ menu->insertItem(i18n("Dock to Panel && Iconify KDevelop"),
+ parent(), SLOT(slotIconifyAndDock()));
+ menu->popup(e->globalPos());
+ } else {
+ moving_ = true;
+ offset_ = parentWidget()->pos() - e->globalPos();
+ setFrameStyle(QFrame::Panel|QFrame::Sunken);
+ QApplication::setOverrideCursor(QCursor(sizeAllCursor));
+ setPalette(QPalette(colorGroup().background()));
+ repaint();
+ }
+}
+
+// **************************************************************************
+
+void DbgMoveHandle::mouseReleaseEvent(QMouseEvent *e)
+{
+ QFrame::mouseReleaseEvent(e);
+ moving_ = false;
+ offset_ = QPoint(0,0);
+ setFrameStyle(QFrame::Panel|QFrame::Raised);
+ QApplication::restoreOverrideCursor();
+ setPalette(QPalette(colorGroup().background()));
+ repaint();
+}
+
+// **************************************************************************
+
+void DbgMoveHandle::mouseMoveEvent(QMouseEvent *e)
+{
+ QFrame::mouseMoveEvent(e);
+ if (!moving_)
+ return;
+
+ toolBar_->move(e->globalPos() + offset_);
+}
+
+// **************************************************************************
+// **************************************************************************
+// **************************************************************************
+
+// This class adds text _and_ a pixmap to a button. Why doesn't QPushButton
+// support that? It only allowed text _or_ pixmap.
+class DbgButton : public QPushButton
+{
+public:
+ DbgButton(const QPixmap &pixmap, const QString &text,
+ DbgToolBar *parent, const char *name=0);
+ virtual ~DbgButton() {};
+ void drawButtonLabel(QPainter *painter);
+ QSize sizeHint() const;
+
+private:
+ QPixmap pixmap_;
+};
+
+// **************************************************************************
+
+DbgButton::DbgButton(const QPixmap& pixmap, const QString& text,
+ DbgToolBar* parent, const char* name)
+ : QPushButton(parent, name),
+ pixmap_(pixmap)
+{
+ setText(text);
+}
+
+// **************************************************************************
+
+void DbgButton::drawButtonLabel(QPainter *painter)
+{
+ // We always have a pixmap (today...)
+ // Centre it if there's no text
+
+ bool hasText = !text().isEmpty();
+ int x = ((hasText ? height() : width()) - pixmap_.width()) / 2;
+ int y = (height() - pixmap_.height()) / 2;
+ painter->drawPixmap(x, y, pixmap_);
+
+ if (hasText) {
+ painter->setPen(colorGroup().text());
+ painter->drawText(height()+2, 0, width()-(height()+2), height(), AlignLeft|AlignVCenter, text());
+ }
+}
+
+// **************************************************************************
+
+QSize DbgButton::sizeHint() const
+{
+ if (text().isEmpty())
+ return pixmap_.size();
+ else
+ return QPushButton::sizeHint();
+}
+
+// **************************************************************************
+// **************************************************************************
+// **************************************************************************
+
+DbgDocker::DbgDocker(QWidget* parent, DbgToolBar* toolBar, const QPixmap& pixmap) :
+ KSystemTray(parent, "DbgDocker"),
+ toolBar_(toolBar)
+{
+ setPixmap(pixmap);
+ QToolTip::add( this, i18n("KDevelop ruby debugger: Click to execute one line of code (\"step\")") );
+}
+
+// **************************************************************************
+
+void DbgDocker::mousePressEvent(QMouseEvent *e)
+{
+ if (!rect().contains( e->pos()))
+ return;
+
+ switch (e->button()) {
+ case LeftButton:
+ {
+ // Not really a click, but it'll hold for the time being !!!
+ emit clicked();
+ break;
+ }
+ case RightButton:
+ {
+ KPopupMenu* menu = new KPopupMenu(this);
+ menu->insertTitle(i18n("Debug Toolbar"));
+ menu->insertItem(i18n("Activate"), toolBar_, SLOT(slotUndock()));
+ menu->insertItem(i18n("Activate (KDevelop gets focus)"), toolBar_, SLOT(slotActivateAndUndock()));
+ menu->popup(e->globalPos());
+ break;
+ }
+ default:
+ break;
+ }
+}
+
+// **************************************************************************
+// **************************************************************************
+// **************************************************************************
+
+DbgToolBar::DbgToolBar(RubyDebuggerPart* part,
+ QWidget* parent, const char* name)
+ : QFrame(0, name),
+ part_(part),
+ activeWindow_(0),
+ winModule_(0),
+ bKDevFocus_(0),
+ bPrevFocus_(0),
+ appIsActive_(false),
+ docked_(false),
+ docker_(0),
+ dockWindow_(new KSystemTray(parent))
+{
+ winModule_ = new KWinModule(this);
+ docker_ = new DbgDocker(parent, this, BarIcon("dbgnext"));
+ connect(docker_, SIGNAL(clicked()), part_, SLOT(slotStepOver()));
+
+ // Must have noFocus set so that we can see what window was active.
+ // see slotDbgKdevFocus() for more comments
+ // I do not want the user to be able to "close" this widget. If we have any
+ // decoration then they can and that is bad.
+ // This widget is closed when the debugger finishes i.e. they press "Stop"
+
+ // Do we need NoFocus???
+ KWin::setState(winId(), NET::StaysOnTop | NET::Modal | NET::SkipTaskbar);
+// KWin::setType(winId(), NET::Override); // So it has no decoration
+ KWin::setType(winId(), NET::Dock);
+
+ setFocusPolicy(NoFocus);
+ setFrameStyle( QFrame::Box | QFrame::Plain );
+ setLineWidth(4);
+ setMidLineWidth(0);
+
+ QBoxLayout* topLayout = new QVBoxLayout(this);
+
+ QBoxLayout* nextLayout = new QHBoxLayout();
+ QBoxLayout* stepLayout = new QHBoxLayout();
+ QBoxLayout* focusLayout = new QHBoxLayout();
+
+ DbgMoveHandle* moveHandle= new DbgMoveHandle(this);
+
+ QPushButton* bRun = new DbgButton(BarIcon("dbgrun"), i18n("Run"), this);
+ QPushButton* bInterrupt = new DbgButton(BarIcon("player_pause"), i18n("Interrupt"), this);
+ QPushButton* bNext = new DbgButton(BarIcon("dbgnext"), i18n("Step Over"), this);
+ QPushButton* bStep = new DbgButton(BarIcon("dbgstep"), i18n("Step Into"), this);
+ QPushButton* bFinish = new DbgButton(BarIcon("dbgstepout"), i18n("Step Out"), this);
+ QPushButton* bRunTo = new DbgButton(BarIcon("dbgrunto"), i18n("Run to Cursor"), this);
+ bPrevFocus_ = new DbgButton(BarIcon("dbgmemview"), QString::null, this);
+ bKDevFocus_ = new DbgButton(BarIcon("kdevelop"), QString::null, this);
+
+ connect(bRun, SIGNAL(clicked()), part_, SLOT(slotRun()));
+ connect(bInterrupt, SIGNAL(clicked()), part_, SLOT(slotPause()));
+ connect(bNext, SIGNAL(clicked()), part_, SLOT(slotStepOver()));
+ connect(bStep, SIGNAL(clicked()), part_, SLOT(slotStepInto()));
+ connect(bFinish, SIGNAL(clicked()), part_, SLOT(slotStepOut()));
+ connect(bRunTo, SIGNAL(clicked()), part_, SLOT(slotRunToCursor()));
+ connect(bKDevFocus_, SIGNAL(clicked()), this, SLOT(slotKdevFocus()));
+ connect(bPrevFocus_, SIGNAL(clicked()), this, SLOT(slotPrevFocus()));
+
+ QToolTip::add( bRun, i18n("Continue with application execution, may start the application") );
+ QToolTip::add( bInterrupt, i18n("Interrupt the application execution") );
+ QToolTip::add( bNext, i18n("Execute one line of code, but run through methods") );
+ QToolTip::add( bStep, i18n("Execute one line of code, stepping into methods if appropriate") );
+ QToolTip::add( bFinish, i18n("Execute to end of current stack frame") );
+ QToolTip::add( bRunTo, i18n("Continues execution until the cursor position is reached.") );
+ QToolTip::add( bKDevFocus_, i18n("Set focus on KDevelop") );
+ QToolTip::add( bPrevFocus_, i18n("Set focus on window that had focus when KDevelop got focus") );
+
+ QWhatsThis::add( bRun, i18n("Continue with application execution. May start the application.") );
+ QWhatsThis::add( bInterrupt, i18n("Interrupt the application execution.") );
+ QWhatsThis::add( bNext, i18n("Execute one line of code, but run through methods.") );
+
+ QWhatsThis::add( bStep, i18n("Execute one line of code, stepping into methods if appropriate.") );
+
+ QWhatsThis::add( bFinish, i18n("Execute to end of current stack frame.") );
+ QWhatsThis::add( bRunTo, i18n("Continues execution until the cursor position is reached.") );
+ QWhatsThis::add( bKDevFocus_, i18n("Set focus on KDevelop.") );
+ QWhatsThis::add( bPrevFocus_, i18n("Set focus on window that had focus when KDevelop got focus.") );
+
+ topLayout->addWidget(moveHandle);
+ topLayout->addWidget(bRun);
+ topLayout->addLayout(nextLayout);
+ topLayout->addLayout(stepLayout);
+ topLayout->addWidget(bFinish);
+ topLayout->addWidget(bRunTo);
+ topLayout->addWidget(bInterrupt);
+ topLayout->addLayout(focusLayout);
+
+ focusLayout->addWidget(bKDevFocus_);
+ focusLayout->addWidget(bPrevFocus_);
+
+ stepLayout->addWidget(bStep);
+
+ nextLayout->addWidget(bNext);
+
+// int w = QMAX(bRun->sizeHint().width(), bFinish->sizeHint().width());
+// w = QMAX(w, bInterrupt->sizeHint().width());
+// w = QMAX(w, bView->sizeHint().width());
+
+ // they should have the same height, so don't be too fussy
+// int h = bFinish->sizeHint().height();
+//
+// bNext->setMinimumHeight(h);
+// bNexti->setMinimumHeight(h);
+// bStep->setMinimumHeight(h);
+// bStepi->setMinimumHeight(h);
+// bKDevFocus_->setMinimumHeight(h);
+// bPrevFocus_->setMinimumHeight(h);
+
+// setMinimumSize(w+10, h*7);
+// setMaximumSize(w+10, h*7);
+
+ setAppIndicator(appIsActive_);
+ topLayout->activate();
+}
+
+// **************************************************************************
+
+DbgToolBar::~DbgToolBar()
+{
+ slotUndock();
+}
+
+// **************************************************************************
+
+void DbgToolBar::slotKdevFocus()
+{
+ // I really want to be able to set the focus on the _application_ being debugged
+ // but this is the best compromise I can come up with. All we do is save the
+ // window that had focus when they switch to the kdevelop window. To do this
+ // the toolbar _cannot_ accept focus.
+ // If anyone has a way of determining what window the app is _actually_ running on
+ // then please fix and send a patch.
+
+ if (winModule_->activeWindow() != topLevelWidget()->winId())
+ activeWindow_ = winModule_->activeWindow();
+
+ KWin::activateWindow(topLevelWidget()->winId());
+}
+
+// **************************************************************************
+
+void DbgToolBar::slotPrevFocus()
+{
+ KWin::activateWindow(activeWindow_);
+}
+
+// **************************************************************************
+
+// If the app is active then the app button is highlighted, otherwise
+// kdev button is highlighted.
+void DbgToolBar::slotDbgStatus(const QString&, int state)
+{
+ bool appIndicator = state & s_appBusy;
+ if (appIndicator != appIsActive_) {
+ setAppIndicator(appIndicator);
+ appIsActive_ = appIndicator;
+ }
+}
+
+// **************************************************************************
+
+void DbgToolBar::setAppIndicator(bool appIndicator)
+{
+ if (appIndicator) {
+ bPrevFocus_->setPalette(QPalette(colorGroup().mid()));
+ bKDevFocus_->setPalette(QPalette(colorGroup().background()));
+ } else {
+ bPrevFocus_->setPalette(QPalette(colorGroup().background()));
+ bKDevFocus_->setPalette(QPalette(colorGroup().mid()));
+ }
+}
+
+// **************************************************************************
+
+void DbgToolBar::slotDock()
+{
+ if (docked_)
+ return;
+
+ // Q_ASSERT(!docker_);
+ hide();
+
+ docker_->show();
+ docked_ = true;
+}
+
+// **************************************************************************
+
+void DbgToolBar::slotIconifyAndDock()
+{
+ if (docked_)
+ return;
+
+ // KWin::iconifyWindow(ckDevelop_->winId(), true);
+ slotDock();
+}
+
+// **************************************************************************
+
+void DbgToolBar::slotUndock()
+{
+ if (!docked_)
+ return;
+
+ show();
+ docker_->hide();
+ docked_ = false;
+}
+
+// **************************************************************************
+
+void DbgToolBar::slotActivateAndUndock()
+{
+ if (!docked_)
+ return;
+
+ KWin::activateWindow(topLevelWidget()->winId());
+ slotUndock();
+}
+
+}
+
+// **************************************************************************
+#include "dbgtoolbar.moc"
diff --git a/languages/ruby/debugger/dbgtoolbar.h b/languages/ruby/debugger/dbgtoolbar.h
new file mode 100644
index 00000000..a19b2eae
--- /dev/null
+++ b/languages/ruby/debugger/dbgtoolbar.h
@@ -0,0 +1,90 @@
+/***************************************************************************
+ begin : Thu Dec 23 1999
+ copyright : (C) 1999 by John Birch
+ email : jbb@kdevelop.org
+
+ Adapted for ruby debugging
+ --------------------------
+ begin : Mon Nov 1 2004
+ copyright : (C) 2004 by Richard Dale
+ email : Richard_Dale@tipitina.demon.co.uk
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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 *q
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#ifndef _DBGTOOLBAR_H_
+#define _DBGTOOLBAR_H_
+
+class KWinModule;
+
+#include <ksystemtray.h>
+#include <kwin.h> // needed for WId :(
+
+#include <qframe.h>
+
+namespace RDBDebugger
+{
+
+class DbgButton;
+class DbgToolBar;
+class RubyDebuggerPart;
+
+class DbgDocker : public KSystemTray
+{
+ Q_OBJECT
+
+public:
+ DbgDocker(QWidget *parent, DbgToolBar *toolBar, const QPixmap &pixmap);
+ virtual ~DbgDocker() {};
+ virtual void mousePressEvent(QMouseEvent *e);
+
+signals:
+ void clicked();
+
+private:
+ DbgToolBar* toolBar_;
+};
+
+
+class DbgToolBar : public QFrame
+{
+ Q_OBJECT
+
+public:
+ DbgToolBar(RubyDebuggerPart *part, QWidget* parent, const char* name=0);
+ virtual ~DbgToolBar();
+
+private slots:
+ void slotDbgStatus(const QString&, int);
+ void slotDock();
+ void slotUndock();
+ void slotIconifyAndDock();
+ void slotActivateAndUndock();
+
+ void slotKdevFocus();
+ void slotPrevFocus();
+
+private:
+ void setAppIndicator(bool appIndicator);
+
+ RubyDebuggerPart* part_;
+ WId activeWindow_;
+ KWinModule* winModule_;
+ DbgButton* bKDevFocus_;
+ DbgButton* bPrevFocus_;
+ bool appIsActive_;
+ bool docked_;
+ DbgDocker* docker_;
+ KSystemTray* dockWindow_;
+};
+
+}
+
+#endif
diff --git a/languages/ruby/debugger/debuggee.rb b/languages/ruby/debugger/debuggee.rb
new file mode 100644
index 00000000..38e2dea7
--- /dev/null
+++ b/languages/ruby/debugger/debuggee.rb
@@ -0,0 +1,1214 @@
+# Copyright (C) 2000 Network Applied Communication Laboratory, Inc.
+# Copyright (C) 2000 Information-technology Promotion Agency, Japan
+# Copyright (C) 2000-2003 NAKAMURA, Hiroshi <nahi@ruby-lang.org>
+
+# Changes for the FreeRIDE IDE by Laurent JULLIARD. FreeRIDE uses
+# Distributed ruby (DRuby) to communicate with the debugger back
+# end. However, this can't interoperate with C++ in KDevelop and so
+# a Unix domain socket connection is used instead.
+
+# Adapted for KDevelop debugging
+# ------------------------------
+# begin : Mon Nov 1 2004
+# copyright : (C) 2004 by Richard Dale
+# email : Richard_Dale@tipitina.demon.co.uk
+
+if $SAFE > 0
+ STDERR.print "-r debug.rb is not available in safe mode\n"
+ exit 1
+end
+
+require 'tracer'
+require 'pp'
+require 'rbconfig'
+
+class Tracer
+ def Tracer.trace_func(*vars)
+ Single.trace_func(*vars)
+ end
+end
+
+# FreeRIDE/KDevelop must always intercept exits hence the exit! redefinition
+# at_exit calls the quit method to cleanly disconnect from the
+# FreeRIDE/KDevelop debugger client
+module Kernel
+ alias_method :exit!, :exit
+end
+
+BEGIN {
+ at_exit do
+ set_trace_func nil
+ DEBUGGER__.quit
+ end
+}
+
+SCRIPT_LINES__ = {} unless defined? SCRIPT_LINES__
+
+class DEBUGGER__
+class Mutex
+ def initialize
+ @locker = nil
+ @waiting = []
+ @locked = false;
+ end
+
+ def locked?
+ @locked
+ end
+
+ def lock
+ return if Thread.critical
+ return if @locker == Thread.current
+ while (Thread.critical = true; @locked)
+ @waiting.push Thread.current
+ Thread.stop
+ end
+ @locked = true
+ @locker = Thread.current
+ Thread.critical = false
+ self
+ end
+
+ def unlock
+ return if Thread.critical
+ return unless @locked
+ unless @locker == Thread.current
+ raise RuntimeError, "unlocked by other"
+ end
+ Thread.critical = true
+ t = @waiting.shift
+ @locked = false
+ @locker = nil
+ Thread.critical = false
+ t.run if t
+ self
+ end
+end
+MUTEX = Mutex.new
+
+class Context
+ DEBUG_LAST_CMD = []
+
+ def readline(prompt_cmd, hist)
+ DEBUGGER__.client.readline(prompt_cmd)
+ end
+
+ def initialize
+ if Thread.current == Thread.main
+ @stop_next = 1
+ else
+ @stop_next = 0
+ end
+ @last_file = nil
+ @file = nil
+ @line = nil
+ @no_step = nil
+ @frames = []
+ @finish_pos = 0
+ @trace = false
+ @trace_ruby = false
+ @catch = "StandardError"
+ @suspend_next = false
+ end
+
+ def stop_next(n=1)
+ @stop_next = n
+ end
+
+ def set_suspend
+ @suspend_next = true
+ end
+
+ def clear_suspend
+ @suspend_next = false
+ end
+
+ def suspend_all
+ DEBUGGER__.suspend
+ end
+
+ def resume_all
+ DEBUGGER__.resume
+ end
+
+ def check_suspend
+ return if Thread.critical
+ while (Thread.critical = true; @suspend_next)
+ DEBUGGER__.waiting.push Thread.current
+ @suspend_next = false
+ Thread.stop
+ end
+ Thread.critical = false
+ end
+
+ def trace?
+ @trace
+ end
+
+ def set_trace(arg)
+ @trace = arg
+ end
+
+ def trace_ruby?
+ @trace_ruby
+ end
+
+ def set_trace_ruby(arg)
+ @trace_ruby = arg
+ end
+
+ def stdout
+ DEBUGGER__.stdout
+ end
+
+ def break_points
+ DEBUGGER__.break_points
+ end
+
+ def display
+ DEBUGGER__.display
+ end
+
+ def context(th)
+ DEBUGGER__.context(th)
+ end
+
+ def set_trace_all(arg)
+ DEBUGGER__.set_trace(arg)
+ end
+
+ def set_last_thread(th)
+ DEBUGGER__.set_last_thread(th)
+ end
+
+ def debug_eval(str, binding)
+ begin
+ val = eval(str, binding)
+ rescue StandardError, ScriptError => e
+ at = eval("caller(1)", binding)
+ stdout.printf "%s:%s\n", at.shift, e.to_s.sub(/\(eval\):1:(in `.*?':)?/, '')
+ for i in at
+ stdout.printf "\tfrom %s\n", i
+ end
+ throw :debug_error
+ end
+ end
+
+ def debug_silent_eval(str, binding)
+ begin
+ eval(str, binding)
+ rescue StandardError, ScriptError
+ nil
+ end
+ end
+
+ # Temporarily change the pretty_print methods to not expand arrays
+ # and hashes, just give the length
+ def customize_debug_pp
+ Array.module_eval %q{
+ def pretty_print(pp)
+ pp.pp "Array (%d element(s))" % length
+ end
+ }
+
+ Hash.module_eval %q{
+ def pretty_print(pp)
+ pp.pp "Hash (%d element(s))" % length
+ end
+ }
+ end
+
+ # Restore the original pretty_print methods for arrays and hashes
+ def restore_debug_pp
+ Array.module_eval %q{
+ def pretty_print(q)
+ q.group(1, '[', ']') {
+ self.each {|v|
+ q.comma_breakable unless q.first?
+ q.pp v
+ }
+ }
+ end
+ }
+ Hash.module_eval %q{
+ def pretty_print(q)
+ q.pp_hash self
+ end
+ }
+ end
+
+ # Prevent the 'var *' commands from expanding Arrays and Hashes
+ # This could be done by redefining inspect, but that would affect
+ # everywhere not just here and in the pp command.
+ def debug_inspect(obj)
+ if obj.kind_of? Array
+ "Array (%d element(s))" % obj.length
+ elsif obj.kind_of? Hash
+ "Hash (%d element(s))" % obj.length
+ elsif obj.kind_of? String
+ str = obj.inspect
+ if str.length > 255
+ "String (length %d)" % obj.length
+ else
+ str
+ end
+ else
+ obj.inspect
+ end
+ end
+
+ def var_list(ary, binding)
+ ary.sort!
+ for v in ary
+ stdout.printf " %s => %s\n", v, debug_inspect(eval(v, binding))
+ end
+ end
+
+ def const_list(ary, obj)
+ ary.sort!
+ for c in ary
+ str = debug_inspect(obj.module_eval(c))
+ if c.to_s != str &&
+ str !~ /^Qt::|^KDE::/ && c.to_s !~ /@@classes$|@@cpp_names$|@@idclass$|@@debug_level$/ &&
+ c.to_s !~ /^DCOPMeta$|^Meta$|SCRIPT_LINES__|TRUE|FALSE|NIL|MatchingData/ &&
+ c.to_s !~ /^PLATFORM$|^RELEASE_DATE$|^VERSION$|SilentClient|SilentObject/ &&
+ c.to_s !~ /^Client$|^Context$|^DEBUG_LAST_CMD$|^MUTEX$|^Mutex$|^SimpleDelegater$|^Delegater$/ &&
+ c.to_s !~ /IPsocket|IPserver|UDPsocket|UDPserver|TCPserver|TCPsocket|UNIXserver|UNIXsocket/
+ if c.to_s == "ENV"
+ stdout.printf " %s => Hash (%d element(s))\n", c, obj.module_eval(c).length
+ else
+ stdout.printf " %s => %s\n", c, str
+ end
+ end
+ end
+ end
+
+ def debug_variable_info(input, binding)
+ case input
+ when /^\s*g(?:lobal)?$/
+ var_list(global_variables, binding)
+
+ when /^\s*l(?:ocal)?$/
+ var_list(eval("local_variables", binding) << "self", binding)
+
+ when /^\s*i(?:nstance)?\s+/
+ obj = debug_eval($', binding)
+ var_list(obj.instance_variables, obj.instance_eval{binding()})
+
+ when /^\s*cl(?:ass)?\s+/
+ obj = debug_eval($', binding)
+ unless obj.kind_of? Module
+ stdout.print "Should be Class/Module: ", $', "\n"
+ else
+ const_list(obj.class_variables, obj)
+ end
+
+ when /^\s*c(?:onst(?:ant)?)?\s+/
+ obj = debug_eval($', binding)
+ unless obj.kind_of? Module
+ stdout.print "Should be Class/Module: ", $', "\n"
+ else
+ const_list(obj.constants, obj)
+ end
+ end
+ end
+
+ def debug_method_info(input, binding)
+ case input
+ when /^i(:?nstance)?\s+/
+ obj = debug_eval($', binding)
+
+ len = 0
+ for v in obj.methods.sort
+ len += v.size + 1
+ if len > 70
+ len = v.size + 1
+ stdout.print "\n"
+ end
+ stdout.print v, " "
+ end
+ stdout.print "\n"
+
+ else
+ obj = debug_eval(input, binding)
+ unless obj.kind_of? Module
+ stdout.print "Should be Class/Module: ", input, "\n"
+ else
+ len = 0
+ for v in obj.instance_methods(false).sort
+ len += v.size + 1
+ if len > 70
+ len = v.size + 1
+ stdout.print "\n"
+ end
+ stdout.print v, " "
+ end
+ stdout.print "\n"
+ end
+ end
+ end
+
+ def thnum
+ num = DEBUGGER__.instance_eval{@thread_list[Thread.current]}
+ unless num
+ DEBUGGER__.make_thread_list
+ num = DEBUGGER__.instance_eval{@thread_list[Thread.current]}
+ end
+ num
+ end
+
+ def debug_command(file, line, id, binding)
+ MUTEX.lock
+ set_last_thread(Thread.current)
+ frame_pos = 0
+ binding_file = file
+ binding_line = line
+ previous_line = nil
+ if ENV['EMACS']
+ stdout.printf "\032\032%s:%d:\n", binding_file, binding_line
+ else
+ stdout.printf "%s:%d:%s", binding_file, binding_line,
+ line_at(binding_file, binding_line)
+ end
+ @frames[0] = [binding, file, line, id]
+ display_expressions(binding)
+ prompt = true
+ while prompt and input = readline("(rdb:%d) "%thnum(), true)
+ catch(:debug_error) do
+ if input == ""
+ next unless DEBUG_LAST_CMD[0]
+ input = DEBUG_LAST_CMD[0]
+ stdout.print input, "\n"
+ else
+ DEBUG_LAST_CMD[0] = input
+ end
+
+ case input
+ when /^\s*trace_ruby(?:\s+(on|off))?$/
+ if defined?( $1 )
+ if $1 == 'on'
+ set_trace_ruby true
+ else
+ set_trace_ruby false
+ end
+ end
+
+ when /^\s*tr(?:ace)?(?:\s+(on|off))?(?:\s+(all))?$/
+ if defined?( $2 )
+ if $1 == 'on'
+ set_trace_all true
+ else
+ set_trace_all false
+ end
+ elsif defined?( $1 )
+ if $1 == 'on'
+ set_trace true
+ else
+ set_trace false
+ end
+ end
+ if trace?
+ stdout.print "Trace on.\n"
+ else
+ stdout.print "Trace off.\n"
+ end
+
+ when /^\s*b(?:reak)?\s+(?:(.+):)?([^.:]+)$/
+ pos = $2
+ if $1
+ klass = debug_silent_eval($1, binding)
+# file = $1
+ file = File.expand_path($1)
+ end
+ if pos =~ /^\d+$/
+ pname = pos
+ pos = pos.to_i
+ else
+ pname = pos = pos.intern.id2name
+ end
+ break_points.push [true, 0, klass || file, pos]
+ stdout.printf "Set breakpoint %d at %s:%s\n", break_points.size, klass || file, pname
+
+ when /^\s*b(?:reak)?\s+(.+)[#.]([^.:]+)$/
+ pos = $2.intern.id2name
+ klass = debug_eval($1, binding)
+ break_points.push [true, 0, klass, pos]
+ stdout.printf "Set breakpoint %d at %s.%s\n", break_points.size, klass, pos
+
+ when /^\s*wat(?:ch)?\s+(.+)$/
+ exp = $1
+ break_points.push [true, 1, exp]
+ stdout.printf "Set watchpoint %d\n", break_points.size, exp
+
+ when /^\s*b(?:reak)?$/
+ if break_points.find{|b| b[1] == 0}
+ n = 1
+ stdout.print "Breakpoints:\n"
+ for b in break_points
+ if b[0] and b[1] == 0
+ stdout.printf " %d %s:%s\n", n, b[2], b[3]
+ end
+ n += 1
+ end
+ end
+ if break_points.find{|b| b[1] == 1}
+ n = 1
+ stdout.print "\n"
+ stdout.print "Watchpoints:\n"
+ for b in break_points
+ if b[0] and b[1] == 1
+ stdout.printf " %d %s\n", n, b[2]
+ end
+ n += 1
+ end
+ end
+ if break_points.size == 0
+ stdout.print "No breakpoints\n"
+ else
+ stdout.print "\n"
+ end
+
+ when /^\s*del(?:ete)?(?:\s+(\d+))?$/
+ pos = $1
+ unless pos
+# input = readline("Clear all breakpoints? (y/n) ", false)
+# if input == "y"
+ for b in break_points
+ b[0] = false
+ end
+# end
+ else
+ pos = pos.to_i
+ if break_points[pos-1]
+ break_points[pos-1][0] = false
+ else
+ stdout.printf "Breakpoint %d is not defined\n", pos
+ end
+ end
+
+ when /^\s*disp(?:lay)?\s+(.+)$/
+ exp = $1
+ display.push [true, exp]
+ stdout.printf "%d: ", display.size
+ display_expression(exp, binding)
+
+ when /^\s*disp(?:lay)?$/
+ display_expressions(binding)
+
+ when /^\s*undisp(?:lay)?(?:\s+(\d+))?$/
+ pos = $1
+ unless pos
+# input = readline("Clear all expressions? (y/n) ", false)
+# if input == "y"
+ for d in display
+ d[0] = false
+ end
+# end
+ else
+ pos = pos.to_i
+ if display[pos-1]
+ display[pos-1][0] = false
+ else
+ stdout.printf "Display expression %d is not defined\n", pos
+ end
+ end
+
+ when /^\s*c(?:ont)?$/
+ prompt = false
+
+ when /^\s*s(?:tep)?(?:\s+(\d+))?$/
+ if $1
+ lev = $1.to_i
+ else
+ lev = 1
+ end
+ @stop_next = lev
+ prompt = false
+
+ when /^\s*n(?:ext)?(?:\s+(\d+))?$/
+ if $1
+ lev = $1.to_i
+ else
+ lev = 1
+ end
+ @stop_next = lev
+ @no_step = @frames.size - frame_pos
+ prompt = false
+
+ when /^\s*w(?:here)?$/, /^\s*f(?:rame)?$/
+ display_frames(frame_pos)
+
+ when /^\s*l(?:ist)?(?:\s+(.+))?$/
+ if not $1
+ b = previous_line ? previous_line + 10 : binding_line - 5
+ e = b + 9
+ elsif $1 == '-'
+ b = previous_line ? previous_line - 10 : binding_line - 5
+ e = b + 9
+ else
+ b, e = $1.split(/[-,]/)
+ if e
+ b = b.to_i
+ e = e.to_i
+ else
+ b = b.to_i - 5
+ e = b + 9
+ end
+ end
+ previous_line = b
+ display_list(b, e, binding_file, binding_line)
+
+ when /^\s*up(?:\s+(\d+))?$/
+ previous_line = nil
+ if $1
+ lev = $1.to_i
+ else
+ lev = 1
+ end
+ frame_pos += lev
+ if frame_pos >= @frames.size
+ frame_pos = @frames.size - 1
+ stdout.print "At toplevel\n"
+ end
+ binding, binding_file, binding_line = @frames[frame_pos]
+ stdout.print format_frame(frame_pos)
+
+ when /^\s*down(?:\s+(\d+))?$/
+ previous_line = nil
+ if $1
+ lev = $1.to_i
+ else
+ lev = 1
+ end
+ frame_pos -= lev
+ if frame_pos < 0
+ frame_pos = 0
+ stdout.print "At stack bottom\n"
+ end
+ binding, binding_file, binding_line = @frames[frame_pos]
+ stdout.print format_frame(frame_pos)
+
+ when /^\s*fin(?:ish)?$/
+ if frame_pos == @frames.size
+ stdout.print "\"finish\" not meaningful in the outermost frame.\n"
+ else
+ @finish_pos = @frames.size - frame_pos
+ frame_pos = 0
+ prompt = false
+ end
+
+ when /^\s*cat(?:ch)?(?:\s+(.+))?$/
+ if $1
+ excn = $1
+ if excn == 'off'
+ @catch = nil
+ stdout.print "Clear catchpoint.\n"
+ else
+ @catch = excn
+ stdout.printf "Set catchpoint %s.\n", @catch
+ end
+ else
+ if @catch
+ stdout.printf "Catchpoint %s.\n", @catch
+ else
+ stdout.print "No catchpoint.\n"
+ end
+ end
+
+ when /^\s*q(?:uit)?$/
+# input = readline("Really quit? (y/n) ", false)
+# if input == "y"
+ exit! # exit -> exit!: No graceful way to stop threads...
+# end
+
+
+ when /^\s*v(?:ar)?\s+/
+ debug_variable_info($', binding)
+
+ when /^\s*m(?:ethod)?\s+/
+ debug_method_info($', binding)
+
+ when /^\s*th(?:read)?\s+/
+ if DEBUGGER__.debug_thread_info($', binding) == :cont
+ prompt = false
+ end
+
+ when /^\s*pp\s+/
+ obj_name = $'
+ obj = debug_eval($', binding)
+ customize_debug_pp
+ if obj.kind_of? Array
+ obj.each_index { |i| stdout.printf "[%d]=%s\n", i.to_s, debug_inspect(obj[i]) }
+ elsif obj.kind_of? Hash or obj_name =~ /^ENV$/
+ # Special case ENV to print like a hash
+ obj.each { |key, value| stdout.printf "[%s]=%s\n", key.inspect, debug_inspect(value) }
+ elsif obj.kind_of?(String) && obj.inspect.length > 255
+ # Assume long strings contain packed data and show them as a
+ # sequence of 12 byte slices in hex
+ i = 0
+ while i < obj.length
+ j = (i + 12 < obj.length ? i + 12 : obj.length) - 1
+ stdout.printf "[%d..%d]=0x", i, j
+ for k in i..j
+ stdout.printf "%2.2x", obj[k]
+ end
+ stdout.printf " %s\n", obj[i..j].dump
+
+ i += 12
+ end
+ else
+ PP.pp(obj, stdout)
+ end
+ restore_debug_pp
+
+ when /^\s*p\s+/
+ stdout.printf "%s\n", debug_eval($', binding).inspect
+
+ when /^\s*h(?:elp)?$/
+ debug_print_help()
+
+ else
+ v = debug_eval(input, binding)
+ stdout.printf "%s\n", v.inspect
+ end
+ end
+ end
+ MUTEX.unlock
+ resume_all
+ end
+
+ def debug_print_help
+ stdout.print <<EOHELP
+Debugger help v.-0.002b
+Commands
+ b[reak] [file|class:]<line|method>
+ b[reak] [class.]<line|method>
+ set breakpoint to some position
+ wat[ch] <expression> set watchpoint to some expression
+ cat[ch] <an Exception> set catchpoint to an exception
+ b[reak] list breakpoints
+ cat[ch] show catchpoint
+ del[ete][ nnn] delete some or all breakpoints
+ disp[lay] <expression> add expression into display expression list
+ undisp[lay][ nnn] delete one particular or all display expressions
+ c[ont] run until program ends or hit breakpoint
+ s[tep][ nnn] step (into methods) one line or till line nnn
+ n[ext][ nnn] go over one line or till line nnn
+ w[here] display frames
+ f[rame] alias for where
+ l[ist][ (-|nn-mm)] list program, - lists backwards
+ nn-mm lists given lines
+ up[ nn] move to higher frame
+ down[ nn] move to lower frame
+ fin[ish] return to outer frame
+ tr[ace] (on|off) set trace mode of current thread
+ tr[ace] (on|off) all set trace mode of all threads
+ q[uit] exit from debugger
+ v[ar] g[lobal] show global variables
+ v[ar] l[ocal] show local variables
+ v[ar] i[nstance] <object> show instance variables of object
+ v[ar] cl[ass] <object> show class variables of object
+ v[ar] c[onst] <object> show constants of object
+ m[ethod] i[nstance] <obj> show methods of object
+ m[ethod] <class|module> show instance methods of class or module
+ th[read] l[ist] list all threads
+ th[read] c[ur[rent]] show current thread
+ th[read] [sw[itch]] <nnn> switch thread context to nnn
+ th[read] stop <nnn> stop thread nnn
+ th[read] resume <nnn> resume thread nnn
+ p expression evaluate expression and print its value
+ h[elp] print this help
+ <everything else> evaluate
+EOHELP
+ end
+
+ def display_expressions(binding)
+ n = 1
+ for d in display
+ if d[0]
+ stdout.printf "%d: ", n
+ display_expression(d[1], binding)
+ end
+ n += 1
+ end
+ end
+
+ def display_expression(exp, binding)
+ stdout.printf "%s = %s\n", exp, debug_silent_eval(exp, binding).to_s
+ end
+
+ def frame_set_pos(file, line)
+ if @frames[0]
+ @frames[0][1] = file
+ @frames[0][2] = line
+ end
+ end
+
+ def display_frames(pos)
+ 0.upto(@frames.size - 1) do |n|
+ if n == pos
+ stdout.print "--> "
+ else
+ stdout.print " "
+ end
+ stdout.print format_frame(n)
+ end
+ end
+
+ def format_frame(pos)
+ bind, file, line, id = @frames[pos]
+ sprintf "#%d %s:%s%s\n", pos + 1, file, line,
+ (id ? ":in `#{id.id2name}'" : "")
+ end
+
+ def display_list(b, e, file, line)
+ stdout.printf "[%d, %d] in %s\n", b, e, file
+ if lines = SCRIPT_LINES__[file] and lines != true
+ n = 0
+ b.upto(e) do |n|
+ if n > 0 && lines[n-1]
+ if n == line
+ stdout.printf "=> %d %s\n", n, lines[n-1].chomp
+ else
+ stdout.printf " %d %s\n", n, lines[n-1].chomp
+ end
+ end
+ end
+ else
+ stdout.printf "No sourcefile available for %s\n", file
+ end
+ end
+
+ def line_at(file, line)
+ lines = SCRIPT_LINES__[file]
+ if lines
+ return "\n" if lines == true
+ line = lines[line-1]
+ return "\n" unless line
+ return line
+ end
+ return "\n"
+ end
+
+ def debug_funcname(id)
+ if id.nil?
+ "toplevel"
+ else
+ id.id2name
+ end
+ end
+
+ def check_break_points(file, klass, pos, binding, id)
+ return false if break_points.empty?
+ n = 1
+ for b in break_points
+ if b[0] # valid
+ if b[1] == 0 # breakpoint
+ if (b[2] == file and b[3] == pos) or
+ (klass and b[2] == klass and b[3] == pos)
+ stdout.printf "Breakpoint %d, %s at %s:%s\n", n, debug_funcname(id), file, pos
+ return true
+ end
+ elsif b[1] == 1 # watchpoint
+ if debug_silent_eval(b[2], binding)
+ stdout.printf "Watchpoint %d, %s at %s:%s\n", n, debug_funcname(id), file, pos
+ return true
+ end
+ end
+ end
+ n += 1
+ end
+ return false
+ end
+
+ def excn_handle(file, line, id, binding)
+ if $!.class <= SystemExit
+ set_trace_func nil
+ exit
+ end
+
+ if @catch and ($!.class.ancestors.find { |e| e.to_s == @catch })
+ stdout.printf "%s:%d: `%s' (%s)\n", file, line, $!, $!.class
+ fs = @frames.size
+ tb = caller(0)[-fs..-1]
+ if tb
+ for i in tb
+ stdout.printf "\tfrom %s\n", i
+ end
+ end
+ suspend_all
+ debug_command(file, line, id, binding)
+ end
+ end
+
+ def trace_func(event, file, line, id, binding, klass)
+ Tracer.trace_func(event, file, line, id, binding, klass) if trace?
+ context(Thread.current).check_suspend
+
+ if not trace_ruby? and
+ ( file =~ /#{Config::CONFIG['sitelibdir']}/ or
+ file =~ /#{Config::CONFIG['rubylibdir']}/ or
+ file =~ %r{/debuggee.rb} )
+ case event
+ when 'line'
+ frame_set_pos(file, line)
+
+ when 'call'
+ @frames.unshift [binding, file, line, id]
+
+ when 'c-call'
+ frame_set_pos(file, line)
+
+ when 'class'
+ @frames.unshift [binding, file, line, id]
+
+ when 'return', 'end'
+ @frames.shift
+
+ when 'end'
+ @frames.shift
+
+ when 'raise'
+ excn_handle(file, line, id, binding)
+
+ end
+ return
+ end
+
+ @file = file
+ @line = line
+ case event
+ when 'line'
+ frame_set_pos(file, line)
+ if !@no_step or @frames.size == @no_step
+ @stop_next -= 1
+ @stop_next = -1 if @stop_next < 0
+ elsif @frames.size < @no_step
+ @stop_next = 0 # break here before leaving...
+ else
+ # nothing to do. skipped.
+ end
+ #LJ reverse the test here because we always want the breakpoint reached
+ # message to be display. if stop_next is null *AND* there is also a break point
+ # the message will never display.
+ if check_break_points(file, nil, line, binding, id) or @stop_next == 0
+ # LJ this test doesn't make sense and cause troubles when
+ # on a line with a recursive call and a breakpoint on it (e.g factorial)
+ # or when in a while loop with one line only inside the loop
+ #
+ # RJD: reinstated the test with a check on whether '@frames.size'
+ # has changed to catch the recursive factorial case LJ describes
+ # above. The while loop problem still exists though
+ if [file, line, @frames.size] == @last
+ @stop_next = 1
+ else
+ @no_step = nil
+ suspend_all
+ debug_command(file, line, id, binding)
+ @last = [file, line, @frames.size]
+ end
+ end
+
+ when 'call'
+ @frames.unshift [binding, file, line, id]
+ if check_break_points(file, klass, id.id2name, binding, id)
+ suspend_all
+ debug_command(file, line, id, binding)
+ end
+
+ when 'c-call'
+ frame_set_pos(file, line)
+
+ when 'class'
+ @frames.unshift [binding, file, line, id]
+
+ when 'return', 'end'
+ if @frames.size == @finish_pos
+ @stop_next = 1
+ @finish_pos = 0
+ end
+ @frames.shift
+
+ when 'end'
+ @frames.shift
+
+ when 'raise'
+ excn_handle(file, line, id, binding)
+
+ end
+ @last_file = file
+ end
+end
+
+trap("INT") { DEBUGGER__.interrupt }
+@last_thread = Thread::main
+@max_thread = 1
+@thread_list = {Thread::main => 1}
+@break_points = []
+@display = []
+@waiting = []
+@stdout = STDOUT
+
+ class SilentObject
+ def method_missing( msg_id, *a, &b ); end
+ end
+ SilentClient = SilentObject.new()
+ @client = SilentClient
+ @attached = false
+
+class << DEBUGGER__
+ def stdout
+ @stdout
+ end
+
+ def stdout=(s)
+ @stdout = s
+ end
+
+ def display
+ @display
+ end
+
+ def break_points
+ @break_points
+ end
+
+ def client
+ @client
+ end
+
+ def set_client( client )
+ @client = client
+ DEBUGGER__.stdout = Tracer.stdout = @client
+ end
+
+ def quit
+ #LJ flush STDOUT and ERR
+# @stdout.print "Quitting debugger"
+ STDERR.flush; STDOUT.flush
+ $stderr.flush; $stdout.flush
+# STDERR.close; STDOUT.close
+ detach
+ #DebugSvr.stop_service
+ end
+
+ def detach
+ @attached = false
+ @client.detach
+ set_client( SilentClient )
+ end
+
+ def waiting
+ @waiting
+ end
+
+ def set_trace( arg )
+ saved_crit = Thread.critical
+ Thread.critical = true
+ make_thread_list
+ for th, in @thread_list
+ context(th).set_trace arg
+ end
+ Thread.critical = saved_crit
+ arg
+ end
+
+ def set_last_thread(th)
+ @last_thread = th
+ end
+
+ def suspend
+ saved_crit = Thread.critical
+ Thread.critical = true
+ make_thread_list
+ for th, in @thread_list
+ next if th == Thread.current
+ context(th).set_suspend
+ end
+ Thread.critical = saved_crit
+ # Schedule other threads to suspend as soon as possible.
+ Thread.pass unless Thread.critical
+ end
+
+ def resume
+ saved_crit = Thread.critical
+ Thread.critical = true
+ make_thread_list
+ for th, in @thread_list
+ next if th == Thread.current
+ context(th).clear_suspend
+ end
+ waiting.each do |th|
+ th.run
+ end
+ waiting.clear
+ Thread.critical = saved_crit
+ # Schedule other threads to restart as soon as possible.
+ Thread.pass
+ end
+
+ def context(thread=Thread.current)
+ c = thread[:__debugger_data__]
+ unless c
+ thread[:__debugger_data__] = c = Context.new
+ end
+ c
+ end
+
+ def interrupt
+ context(@last_thread).stop_next
+ end
+
+ def get_thread(num)
+ th = @thread_list.index(num)
+ unless th
+ @stdout.print "No thread ##{num}\n"
+ throw :debug_error
+ end
+ th
+ end
+
+ def thread_list(num)
+ th = get_thread(num)
+ if th == Thread.current
+ @stdout.print "+"
+ else
+ @stdout.print " "
+ end
+ @stdout.printf "%d ", num
+ @stdout.print th.inspect, "\t"
+ file = context(th).instance_eval{@file}
+ if file
+ @stdout.print file,":",context(th).instance_eval{@line}
+ end
+ @stdout.print "\n"
+ end
+
+ def thread_list_all
+ for th in @thread_list.values.sort
+ thread_list(th)
+ end
+ end
+
+ def make_thread_list
+ hash = {}
+ for th in Thread::list
+ if @thread_list.key? th
+ hash[th] = @thread_list[th]
+ else
+ @max_thread += 1
+ hash[th] = @max_thread
+ end
+ end
+ @thread_list = hash
+ end
+
+ def debug_thread_info(input, binding)
+ case input
+ when /^l(?:ist)?/
+ make_thread_list
+ thread_list_all
+
+ when /^c(?:ur(?:rent)?)?$/
+ make_thread_list
+ thread_list(@thread_list[Thread.current])
+
+ when /^(?:sw(?:itch)?\s+)?(\d+)/
+ make_thread_list
+ th = get_thread($1.to_i)
+ if th == Thread.current
+ @stdout.print "It's the current thread.\n"
+ else
+ thread_list(@thread_list[th])
+ context(th).stop_next
+ th.run
+ return :cont
+ end
+
+ when /^stop\s+(\d+)/
+ make_thread_list
+ th = get_thread($1.to_i)
+ if th == Thread.current
+ @stdout.print "It's the current thread.\n"
+ elsif th.stop?
+ @stdout.print "Already stopped.\n"
+ else
+ thread_list(@thread_list[th])
+ context(th).suspend
+ end
+
+ when /^resume\s+(\d+)/
+ make_thread_list
+ th = get_thread($1.to_i)
+ if th == Thread.current
+ @stdout.print "It's the current thread.\n"
+ elsif !th.stop?
+ @stdout.print "Already running."
+ else
+ thread_list(@thread_list[th])
+ th.run
+ end
+ end
+ end
+end
+
+require 'socket'
+
+ ##
+ # DEBUGGEE -> socket -> KDevelop
+ # The Client class holds all the methods invoked from the debuggee that send and
+ # receive data from KDevelop via the Unix domain socket.
+ #
+ class Client
+ def initialize(path)
+ @debugger = UNIXSocket.open(path)
+ @debugger.sync=true
+ end
+
+ def detach
+# @debugger.close
+ end
+
+ def printf( *args )
+ str = sprintf(*args)
+ @debugger.send(str, 0)
+ end
+
+ def print( *args )
+ str = args.to_s
+ @debugger.send(str, 0)
+ end
+
+ def <<( arg )
+ @debugger.send(arg, 0)
+ end
+
+ # Return next command
+ def readline(prompt_cmd)
+ @debugger.send(prompt_cmd, 0)
+ msg = @debugger.recvfrom(2048)
+ return msg[0]
+ end
+ end
+
+#stdout.printf "Debug.rb\n"
+#stdout.printf "Emacs support available.\n\n"
+
+STDERR.sync=true
+STDOUT.sync=true
+
+path = $stdin.gets.chomp
+
+DEBUGGER__.set_client( Client.new(path) )
+
+set_trace_func proc { |event, file, line, id, binding, klass, *rest|
+
+ # LJ make sure the file path is always absolute. It is needed by
+ # the Debugger plugin in KDevelop and can only be determined here
+ # in the context of the debugged process
+ file = File.expand_path(file)
+
+ DEBUGGER__.context.trace_func event, file, line, id, binding, klass
+}
+
+end
diff --git a/languages/ruby/debugger/debuggerpart.cpp b/languages/ruby/debugger/debuggerpart.cpp
new file mode 100644
index 00000000..95b4dd09
--- /dev/null
+++ b/languages/ruby/debugger/debuggerpart.cpp
@@ -0,0 +1,785 @@
+/***************************************************************************
+ * Copyright (C) 1999-2001 by John Birch *
+ * jbb@kdevelop.org *
+ * Copyright (C) 2001 by Bernd Gehrmann *
+ * bernd@kdevelop.org *
+ * *
+ * Adapted for ruby debugging *
+ * -------------------------- *
+ * begin : Mon Nov 1 2004 *
+ * copyright : (C) 2004 by Richard Dale *
+ * email : Richard_Dale@tipitina.demon.co.uk *
+ * *
+ * 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 "debuggerpart.h"
+
+#include <qdir.h>
+#include <qvbox.h>
+#include <qwhatsthis.h>
+#include <qpopupmenu.h>
+
+#include <kaction.h>
+#include <kdebug.h>
+#include <kfiledialog.h>
+#include <kdevgenericfactory.h>
+#include <kiconloader.h>
+#include <klocale.h>
+#include <kmainwindow.h>
+#include <kstatusbar.h>
+#include <kparts/part.h>
+#include <ktexteditor/viewcursorinterface.h>
+#include <kmessagebox.h>
+#include <kapplication.h>
+#include <dcopclient.h>
+#include <qtimer.h>
+#include <kstringhandler.h>
+#include <kstandarddirs.h>
+
+#include "kdevcore.h"
+#include "kdevproject.h"
+#include "kdevmainwindow.h"
+#include "kdevappfrontend.h"
+#include "kdevpartcontroller.h"
+#include "kdevdebugger.h"
+#include "domutil.h"
+#include "variablewidget.h"
+#include "rdbbreakpointwidget.h"
+#include "framestackwidget.h"
+#include "processwidget.h"
+#include "rdbcontroller.h"
+#include "breakpoint.h"
+#include "dbgpsdlg.h"
+#include "dbgtoolbar.h"
+#include "rdbparser.h"
+#include "rdboutputwidget.h"
+#include "processlinemaker.h"
+
+#include <iostream>
+
+#include <kdevplugininfo.h>
+#include <debugger.h>
+
+
+namespace RDBDebugger
+{
+static const KDevPluginInfo data("kdevrbdebugger");
+
+typedef KDevGenericFactory<RubyDebuggerPart> RubyDebuggerFactory;
+K_EXPORT_COMPONENT_FACTORY( libkdevrbdebugger, RubyDebuggerFactory( data ) )
+
+RubyDebuggerPart::RubyDebuggerPart( QObject *parent, const char *name, const QStringList & ) :
+ KDevPlugin( &data, parent, name ? name : "RubyDebuggerPart" ),
+ controller(0)
+{
+// setObjId("RubyDebuggerInterface");
+ setInstance(RubyDebuggerFactory::instance());
+
+ setXMLFile("kdevrbdebugger.rc");
+
+ m_debugger = new Debugger( partController() );
+
+ statusBarIndicator = new QLabel(" ", mainWindow()->statusBar());
+ statusBarIndicator->setFixedWidth(15);
+ mainWindow()->statusBar()->addWidget(statusBarIndicator, 0, true);
+ statusBarIndicator->show();
+
+ // Setup widgets and dbgcontroller
+ variableWidget = new VariableWidget( 0, "rdbVariablewidget");
+// /*variableWidget*/->setEnabled(false);
+ variableWidget->setIcon(SmallIcon("math_brace"));
+ variableWidget->setCaption(i18n("Variable Tree"));
+ QWhatsThis::add
+ (variableWidget, i18n("<b>Variable tree</b><p>"
+ "The variable tree allows you to see "
+ "the variable values as you step "
+ "through your program using the internal "
+ "debugger. Click the right mouse button on items in "
+ "this view to get a popup menu.\n"
+ "To speed up stepping through your code "
+ "leave the tree items closed.\n"));
+ mainWindow()->embedSelectView(variableWidget, i18n("Variables"), i18n("Debugger variable-view"));
+
+// mainWindow()->setViewAvailable(variableWidget, false);
+
+ rdbBreakpointWidget = new RDBBreakpointWidget( 0, "rdbBreakpointWidget" );
+ rdbBreakpointWidget->setCaption(i18n("Breakpoint List"));
+ QWhatsThis::add
+ (rdbBreakpointWidget, i18n("<b>Breakpoint list</b><p>"
+ "Displays a list of breakpoints with "
+ "their current status. Clicking on a "
+ "breakpoint item allows you to change "
+ "the breakpoint and will take you "
+ "to the source in the editor window."));
+ rdbBreakpointWidget->setIcon( SmallIcon("stop") );
+ mainWindow()->embedOutputView(rdbBreakpointWidget, i18n("Breakpoints"), i18n("Debugger breakpoints"));
+
+ framestackWidget = new FramestackWidget( 0, "rdbFramestackWidget" );
+ framestackWidget->setEnabled(false);
+ framestackWidget->setCaption(i18n("Frame Stack"));
+ QWhatsThis::add
+ (framestackWidget, i18n("<b>Frame stack</b><p>"
+ "Often referred to as the \"call stack\", "
+ "this is a list showing what method is "
+ "currently active and who called each "
+ "method to get to this point in your "
+ "program. By clicking on an item you "
+ "can see the values in any of the "
+ "previous calling methods."));
+ framestackWidget->setIcon( SmallIcon("table") );
+ mainWindow()->embedOutputView(framestackWidget, i18n("Frame Stack"), i18n("Debugger method call stack"));
+ mainWindow()->setViewAvailable(framestackWidget, false);
+
+
+ rdbOutputWidget = new RDBOutputWidget( 0, "rdbOutputWidget" );
+ rdbOutputWidget->setEnabled(false);
+ rdbOutputWidget->setIcon( SmallIcon("inline_image") );
+ rdbOutputWidget->setCaption(i18n("RDB Output"));
+ QWhatsThis::add
+ (rdbOutputWidget, i18n("<b>RDB output</b><p>"
+ "Shows all rdb commands being executed. "
+ "You can also issue any other rdb command while debugging."));
+ mainWindow()->embedOutputView(rdbOutputWidget, i18n("RDB"),
+ i18n("RDB output"));
+ mainWindow()->setViewAvailable(rdbOutputWidget, false);
+
+ // rdbBreakpointWidget -> this
+ connect( rdbBreakpointWidget, SIGNAL(refreshBPState(const Breakpoint&)),
+ this, SLOT(slotRefreshBPState(const Breakpoint&)));
+ connect( rdbBreakpointWidget, SIGNAL(publishBPState(const Breakpoint&)),
+ this, SLOT(slotRefreshBPState(const Breakpoint&)));
+ connect( rdbBreakpointWidget, SIGNAL(gotoSourcePosition(const QString&, int)),
+ this, SLOT(slotGotoSource(const QString&, int)) );
+
+ // Now setup the actions
+ KAction *action;
+
+// action = new KAction(i18n("&Start"), "1rightarrow", CTRL+SHIFT+Key_F9,
+ action = new KAction(i18n("&Start"), "dbgrun", CTRL+SHIFT+Key_F9,
+ this, SLOT(slotRun()),
+ actionCollection(), "debug_run");
+ action->setToolTip( i18n("Start in debugger") );
+ action->setWhatsThis( i18n("<b>Start in debugger</b><p>"
+ "Starts the debugger with the project's main "
+ "executable. You may set some breakpoints "
+ "before this, or you can interrupt the program "
+ "while it is running, in order to get information "
+ "about variables, frame stack, and so on.") );
+
+ action = new KAction(i18n("Sto&p"), "stop", 0,
+ this, SLOT(slotStop()),
+ actionCollection(), "debug_stop");
+ action->setToolTip( i18n("Stop debugger") );
+ action->setWhatsThis(i18n("<b>Stop debugger</b><p>Kills the executable and exits the debugger."));
+
+ action = new KAction(i18n("Interrupt"), "player_pause", 0,
+ this, SLOT(slotPause()),
+ actionCollection(), "debug_pause");
+ action->setToolTip( i18n("Interrupt application") );
+ action->setWhatsThis(i18n("<b>Interrupt application</b><p>Interrupts the debugged process or current RDB command."));
+
+ action = new KAction(i18n("Run to &Cursor"), "dbgrunto", 0,
+ this, SLOT(slotRunToCursor()),
+ actionCollection(), "debug_runtocursor");
+ action->setToolTip( i18n("Run to cursor") );
+ action->setWhatsThis(i18n("<b>Run to cursor</b><p>Continues execution until the cursor position is reached."));
+
+
+ action = new KAction(i18n("Step &Over"), "dbgnext", 0,
+ this, SLOT(slotStepOver()),
+ actionCollection(), "debug_stepover");
+ action->setToolTip( i18n("Step over the next line") );
+ action->setWhatsThis( i18n("<b>Step over</b><p>"
+ "Executes one line of source in the current source file. "
+ "If the source line is a call to a method the whole "
+ "method is executed and the app will stop at the line "
+ "following the method call.") );
+
+
+ action = new KAction(i18n("Step &Into"), "dbgstep", 0,
+ this, SLOT(slotStepInto()),
+ actionCollection(), "debug_stepinto");
+ action->setToolTip( i18n("Step into the next statement") );
+ action->setWhatsThis( i18n("<b>Step into</b><p>"
+ "Executes exactly one line of source. If the source line "
+ "is a call to a method then execution will stop after "
+ "the method has been entered.") );
+
+
+
+ action = new KAction(i18n("Step O&ut"), "dbgstepout", 0,
+ this, SLOT(slotStepOut()),
+ actionCollection(), "debug_stepout");
+ action->setToolTip( i18n("Steps out of the current method") );
+ action->setWhatsThis( i18n("<b>Step out</b><p>"
+ "Executes the application until the currently executing "
+ "method is completed. The debugger will then display "
+ "the line after the original call to that method. If "
+ "program execution is in the outermost frame (i.e. in "
+ "the topleveltoggleWatchpoint) then this operation has no effect.") );
+
+
+ action = new KAction(i18n("Toggle Breakpoint"), 0, 0,
+ this, SLOT(toggleBreakpoint()),
+ actionCollection(), "debug_toggle_breakpoint");
+ action->setToolTip(i18n("Toggle breakpoint"));
+ action->setWhatsThis(i18n("<b>Toggle breakpoint</b><p>Toggles the breakpoint at the current line in editor."));
+
+ connect( mainWindow()->main()->guiFactory(), SIGNAL(clientAdded(KXMLGUIClient*)),
+ this, SLOT(guiClientAdded(KXMLGUIClient*)) );
+
+
+ connect( partController(), SIGNAL(loadedFile(const KURL &)),
+ rdbBreakpointWidget, SLOT(slotRefreshBP(const KURL &)) );
+ connect( debugger(), SIGNAL(toggledBreakpoint(const QString &, int)),
+ rdbBreakpointWidget, SLOT(slotToggleBreakpoint(const QString &, int)) );
+ connect( debugger(), SIGNAL(editedBreakpoint(const QString &, int)),
+ rdbBreakpointWidget, SLOT(slotEditBreakpoint(const QString &, int)) );
+ connect( debugger(), SIGNAL(toggledBreakpointEnabled(const QString &, int)),
+ rdbBreakpointWidget, SLOT(slotToggleBreakpointEnabled(const QString &, int)) );
+
+ connect( core(), SIGNAL(contextMenu(QPopupMenu *, const Context *)),
+ this, SLOT(contextMenu(QPopupMenu *, const Context *)) );
+
+ connect( core(), SIGNAL(stopButtonClicked(KDevPlugin*)),
+ this, SLOT(slotStop(KDevPlugin*)) );
+ connect( core(), SIGNAL(projectClosed()),
+ this, SLOT(projectClosed()) );
+
+ connect( partController(), SIGNAL(activePartChanged(KParts::Part*)),
+ this, SLOT(slotActivePartChanged(KParts::Part*)) );
+
+ procLineMaker = new ProcessLineMaker();
+
+ connect( procLineMaker, SIGNAL(receivedStdoutLine(const QCString&)),
+ appFrontend(), SLOT(insertStdoutLine(const QCString&)) );
+ connect( procLineMaker, SIGNAL(receivedStderrLine(const QCString&)),
+ appFrontend(), SLOT(insertStderrLine(const QCString&)) );
+ connect( procLineMaker, SIGNAL(receivedPartialStdoutLine(const QCString&)),
+ appFrontend(), SLOT(addPartialStdoutLine(const QCString&)) );
+ connect( procLineMaker, SIGNAL(receivedPartialStderrLine(const QCString&)),
+ appFrontend(), SLOT(addPartialStderrLine(const QCString&)) );
+
+ setupController();
+ QTimer::singleShot(0, this, SLOT(setupDcop()));
+}
+
+RubyDebuggerPart::~RubyDebuggerPart()
+{
+ kapp->dcopClient()->setNotifications(false);
+
+ if (variableWidget)
+ mainWindow()->removeView(variableWidget);
+ if (rdbBreakpointWidget)
+ mainWindow()->removeView(rdbBreakpointWidget);
+ if (framestackWidget)
+ mainWindow()->removeView(framestackWidget);
+ if(rdbOutputWidget)
+ mainWindow()->removeView(rdbOutputWidget);
+
+ delete variableWidget;
+ delete rdbBreakpointWidget;
+ delete framestackWidget;
+ delete rdbOutputWidget;
+ delete controller;
+ delete floatingToolBar;
+ delete statusBarIndicator;
+ delete procLineMaker;
+}
+
+
+void RubyDebuggerPart::guiClientAdded( KXMLGUIClient* client )
+{
+ // Can't change state until after XMLGUI has been loaded...
+ // Anyone know of a better way of doing this?
+ if( client == this )
+ stateChanged( QString("stopped") );
+}
+
+void RubyDebuggerPart::contextMenu(QPopupMenu *popup, const Context *context)
+{
+ if (!context->hasType( Context::EditorContext ))
+ return;
+
+ const EditorContext *econtext = static_cast<const EditorContext*>(context);
+ m_contextIdent = econtext->currentWord();
+
+ popup->insertSeparator();
+ if (econtext->url().isLocalFile())
+ {
+ int id = popup->insertItem( i18n("Toggle Breakpoint"), this, SLOT(toggleBreakpoint()) );
+ popup->setWhatsThis(id, i18n("<b>Toggle breakpoint</b><p>Toggles breakpoint at the current line."));
+ }
+ if (!m_contextIdent.isEmpty())
+ {
+ QString squeezed = KStringHandler::csqueeze(m_contextIdent, 30);
+ int id = popup->insertItem( i18n("Watch: %1").arg(squeezed), this, SLOT(contextWatch()) );
+ popup->setWhatsThis(id, i18n("<b>Watch</b><p>Adds an expression under the cursor to the Variables/Watch list."));
+
+ id = popup->insertItem( i18n("Inspect: %1").arg(squeezed), this, SLOT(contextRubyInspect()) );
+ popup->setWhatsThis(id, i18n("<b>Inspect</b><p>Evaluates an expression under the cursor."));
+ }
+}
+
+
+void RubyDebuggerPart::toggleBreakpoint()
+{
+ KParts::ReadWritePart *rwpart
+ = dynamic_cast<KParts::ReadWritePart*>(partController()->activePart());
+ KTextEditor::ViewCursorInterface *cursorIface
+ = dynamic_cast<KTextEditor::ViewCursorInterface*>(partController()->activeWidget());
+
+ if (!rwpart || !cursorIface)
+ return;
+
+ uint line, col;
+ cursorIface->cursorPositionReal(&line, &col);
+
+ rdbBreakpointWidget->slotToggleBreakpoint(rwpart->url().path(), line);
+}
+
+
+void RubyDebuggerPart::contextWatch()
+{
+ variableWidget->slotAddWatchExpression(m_contextIdent);
+}
+
+// Evaluates the selected text
+void RubyDebuggerPart::contextRubyInspect()
+{
+ emit rubyInspect(m_contextIdent);
+}
+
+
+void RubyDebuggerPart::setupController()
+{
+ VariableTree *variableTree = variableWidget->varTree();
+
+ controller = new RDBController(variableTree, framestackWidget, *projectDom());
+
+ // this -> controller
+ connect( this, SIGNAL(rubyInspect(const QString&)),
+ controller, SLOT(slotRubyInspect(const QString&)));
+
+ // variableTree -> framestackWidget
+ connect( variableTree, SIGNAL(selectFrame(int, int)),
+ framestackWidget, SLOT(slotSelectFrame(int, int)));
+
+ // framestackWidget -> variableTree
+ connect( framestackWidget, SIGNAL(frameActive(int, int, const QString&)),
+ variableTree, SLOT(slotFrameActive(int, int, const QString&)));
+
+ // variableTree -> controller
+ connect( variableTree, SIGNAL(expandItem(VarItem*, const QCString&)),
+ controller, SLOT(slotExpandItem(VarItem*, const QCString&)));
+ connect( variableTree, SIGNAL(fetchGlobals(bool)),
+ controller, SLOT(slotFetchGlobals(bool)));
+ connect( variableTree, SIGNAL(addWatchExpression(const QString&, bool)),
+ controller, SLOT(slotAddWatchExpression(const QString&, bool)));
+ connect( variableTree, SIGNAL(removeWatchExpression(int)),
+ controller, SLOT(slotRemoveWatchExpression(int)));
+
+ // framestackWidget -> controller
+ connect( framestackWidget, SIGNAL(selectFrame(int,int,const QString&)),
+ controller, SLOT(slotSelectFrame(int,int,const QString&)));
+
+ // rdbBreakpointWidget -> controller
+ connect( rdbBreakpointWidget, SIGNAL(clearAllBreakpoints()),
+ controller, SLOT(slotClearAllBreakpoints()));
+ connect( rdbBreakpointWidget, SIGNAL(publishBPState(const Breakpoint&)),
+ controller, SLOT(slotBPState(const Breakpoint &)));
+
+
+ // rdbOutputWidget -> controller
+ connect( rdbOutputWidget, SIGNAL(userRDBCmd(const QString &)),
+ controller, SLOT(slotUserRDBCmd(const QString&)));
+ connect( rdbOutputWidget, SIGNAL(breakInto()),
+ controller, SLOT(slotBreakInto()));
+
+ // controller -> rdbBreakpointWidget
+ connect( controller, SIGNAL(acceptPendingBPs()),
+ rdbBreakpointWidget, SLOT(slotSetPendingBPs()));
+ connect( controller, SIGNAL(unableToSetBPNow(int)),
+ rdbBreakpointWidget, SLOT(slotUnableToSetBPNow(int)));
+ connect( controller, SIGNAL(rawRDBBreakpointList (char*)),
+ rdbBreakpointWidget, SLOT(slotParseRDBBrkptList(char*)));
+ connect( controller, SIGNAL(rawRDBBreakpointSet(char*, int)),
+ rdbBreakpointWidget, SLOT(slotParseRDBBreakpointSet(char*, int)));
+
+
+ // controller -> this
+ connect( controller, SIGNAL(dbgStatus(const QString&, int)),
+ this, SLOT(slotStatus(const QString&, int)));
+ connect( controller, SIGNAL(showStepInSource(const QString&, int, const QString&)),
+ this, SLOT(slotShowStep(const QString&, int)));
+
+ // controller -> procLineMaker
+ connect( controller, SIGNAL(ttyStdout(const char*)),
+ procLineMaker, SLOT(slotReceivedStdout(const char*)));
+ connect( controller, SIGNAL(ttyStderr(const char*)),
+ procLineMaker, SLOT(slotReceivedStderr(const char*)));
+
+ // controller -> rdbOutputWidget
+ connect( controller, SIGNAL(rdbStdout(const char*)),
+ rdbOutputWidget, SLOT(slotReceivedStdout(const char*)) );
+ connect( controller, SIGNAL(rdbStderr(const char*)),
+ rdbOutputWidget, SLOT(slotReceivedStderr(const char*)) );
+ connect( controller, SIGNAL(dbgStatus(const QString&, int)),
+ rdbOutputWidget, SLOT(slotDbgStatus(const QString&, int)));
+
+}
+
+
+bool RubyDebuggerPart::startDebugger()
+{
+ QString build_dir; // Currently selected build directory
+ QString run_directory; // Directory from where the program should be run
+ QString program; // Absolute path to application
+ QString run_arguments; // Command line arguments to be passed to the application
+ QString ruby_interpreter; // Absolute path to the ruby interpreter
+ QString debuggee_path; // Absolute path to debuggee.rb debugger script
+ bool show_constants; // Show constants in the debugger
+ bool trace_into_ruby; // Trace into the ruby code installed under sitedir
+
+ if (project()) {
+ build_dir = project()->buildDirectory();
+ run_directory = DomUtil::readEntry(*projectDom(), "/kdevscriptproject/run/globalcwd");
+ if (run_directory.isEmpty())
+ run_directory = project()->buildDirectory();
+ }
+
+ int runMainProgram = DomUtil::readIntEntry(*projectDom(), "/kdevrubysupport/run/runmainprogram");
+
+ if (runMainProgram == 0) {
+ program = project()->projectDirectory() + "/" + DomUtil::readEntry(*projectDom(), "/kdevrubysupport/run/mainprogram");
+ } else {
+ KParts::ReadOnlyPart *ro_part = dynamic_cast<KParts::ReadOnlyPart*>(partController()->activePart());
+ if (ro_part != 0) {
+ program = ro_part->url().path();
+ }
+ }
+
+ run_arguments = DomUtil::readEntry(*projectDom(), "/kdevrubysupport/run/programargs");
+
+ QString shell = DomUtil::readEntry(*projectDom(), "/kdevrbdebugger/general/dbgshell");
+ if( !shell.isEmpty() )
+ {
+ QFileInfo info( shell );
+ if( info.isRelative() )
+ {
+ shell = build_dir + "/" + shell;
+ info.setFile( shell );
+ }
+ if( !info.exists() )
+ {
+ KMessageBox::error(
+ mainWindow()->main(),
+ i18n("Could not locate the debugging shell '%1'.").arg( shell ),
+ i18n("Debugging Shell Not Found") );
+ return false;
+ }
+ }
+
+ core()->running(this, true);
+
+ stateChanged( QString("active") );
+
+ KActionCollection *ac = actionCollection();
+ ac->action("debug_run")->setText( i18n("&Continue") );
+// ac->action("debug_run")->setIcon( "dbgrun" );
+ ac->action("debug_run")->setToolTip( i18n("Continues the application execution") );
+ ac->action("debug_run")->setWhatsThis( i18n("Continue application execution\n\n"
+ "Continues the execution of your application in the "
+ "debugger. This only takes effect when the application "
+ "has been halted by the debugger (i.e. a breakpoint has "
+ "been activated or the interrupt was pressed).") );
+
+
+// mainWindow()->setViewAvailable(variableWidget, true);
+ mainWindow()->setViewAvailable(framestackWidget, true);
+ mainWindow()->setViewAvailable(rdbOutputWidget, true);
+
+// variableWidget->setEnabled(true);
+ framestackWidget->setEnabled(true);
+
+ rdbOutputWidget->clear();
+ rdbOutputWidget->setEnabled(true);
+
+ if (DomUtil::readBoolEntry(*projectDom(), "/kdevrbdebugger/general/floatingtoolbar", false))
+ {
+ floatingToolBar = new DbgToolBar(this, mainWindow()->main());
+ floatingToolBar->show();
+ }
+
+ ruby_interpreter = DomUtil::readEntry(*projectDom(), "/kdevrubysupport/run/interpreter");
+
+ int coding = DomUtil::readIntEntry(*projectDom(), "/kdevrubysupport/run/charactercoding");
+ QString character_coding("-K");
+
+ switch (coding) {
+ case 0:
+ character_coding.append("A");
+ break;
+ case 1:
+ character_coding.append("E");
+ break;
+ case 2:
+ character_coding.append("S");
+ break;
+ case 3:
+ character_coding.append("U");
+ break;
+ }
+
+// ruby_interpreter.append(QString(" -K") + code);
+
+ debuggee_path = ::locate("data", "kdevrbdebugger/debuggee.rb", instance());
+
+ show_constants = DomUtil::readBoolEntry(*projectDom(), "/kdevrbdebugger/general/showconstants");
+ trace_into_ruby = DomUtil::readBoolEntry(*projectDom(), "/kdevrbdebugger/general/traceintoruby");
+
+ controller->slotStart(ruby_interpreter, character_coding, run_directory, debuggee_path, program, run_arguments, show_constants, trace_into_ruby);
+ return true;
+}
+
+void RubyDebuggerPart::slotStopDebugger()
+{
+ controller->slotStopDebugger();
+ debugger()->clearExecutionPoint();
+
+ delete floatingToolBar;
+ floatingToolBar = 0;
+
+ rdbBreakpointWidget->reset();
+ framestackWidget->clear();
+ variableWidget->varTree()->clear();
+
+// variableWidget->setEnabled(false);
+ framestackWidget->setEnabled(false);
+ rdbOutputWidget->setEnabled(false);
+
+// mainWindow()->setViewAvailable(variableWidget, false);
+ mainWindow()->setViewAvailable(framestackWidget, false);
+ mainWindow()->setViewAvailable(rdbOutputWidget, false);
+
+ KActionCollection *ac = actionCollection();
+ ac->action("debug_run")->setText( i18n("&Start") );
+// ac->action("debug_run")->setIcon( "1rightarrow" );
+ ac->action("debug_run")->setToolTip( i18n("Runs the program in the debugger") );
+ ac->action("debug_run")->setWhatsThis( i18n("Start in debugger\n\n"
+ "Starts the debugger with the project's main "
+ "executable. You may set some breakpoints "
+ "before this, or you can interrupt the program "
+ "while it is running, in order to get information "
+ "about variables, frame stack, and so on.") );
+
+ stateChanged( QString("stopped") );
+
+ core()->running(this, false);
+}
+
+void RubyDebuggerPart::projectClosed()
+{
+ slotStopDebugger();
+}
+
+void RubyDebuggerPart::slotRun()
+{
+ if (controller->stateIsOn(s_programExited)) {
+ rdbBreakpointWidget->reset();
+ }
+
+ if ( controller->stateIsOn( s_dbgNotStarted ) ) {
+ mainWindow()->statusBar()->message(i18n("Debugging program"), 1000);
+ mainWindow()->raiseView(rdbOutputWidget);
+ appFrontend()->clearView();
+ startDebugger();
+ } else {
+ KActionCollection *ac = actionCollection();
+ ac->action("debug_run")->setText( i18n("&Continue") );
+ ac->action("debug_run")->setToolTip( i18n("Continues the application execution") );
+ ac->action("debug_run")->setWhatsThis( i18n("Continue application execution\n\n"
+ "Continues the execution of your application in the "
+ "debugger. This only takes effect when the application "
+ "has been halted by the debugger (i.e. a breakpoint has "
+ "been activated or the interrupt was pressed).") );
+
+ mainWindow()->statusBar()->message(i18n("Continuing program"), 1000);
+ }
+
+ controller->slotRun();
+}
+
+
+
+void RubyDebuggerPart::slotStop(KDevPlugin* which)
+{
+ if( which != 0 && which != this )
+ return;
+
+// if( !controller->stateIsOn( s_dbgNotStarted ) && !controller->stateIsOn( s_shuttingDown ) )
+ slotStopDebugger();
+}
+
+
+void RubyDebuggerPart::slotPause()
+{
+ controller->slotBreakInto();
+}
+
+
+void RubyDebuggerPart::slotRunToCursor()
+{
+ KParts::ReadWritePart *rwpart
+ = dynamic_cast<KParts::ReadWritePart*>(partController()->activePart());
+ KTextEditor::ViewCursorInterface *cursorIface
+ = dynamic_cast<KTextEditor::ViewCursorInterface*>(partController()->activeWidget());
+
+ if (!rwpart || !rwpart->url().isLocalFile() || !cursorIface)
+ return;
+
+ uint line, col;
+ cursorIface->cursorPosition(&line, &col);
+
+ controller->slotRunUntil(rwpart->url().path(), line);
+}
+
+void RubyDebuggerPart::slotStepOver()
+{
+ controller->slotStepOver();
+}
+
+
+
+void RubyDebuggerPart::slotStepInto()
+{
+ controller->slotStepInto();
+}
+
+
+void RubyDebuggerPart::slotStepOut()
+{
+ controller->slotStepOutOff();
+}
+
+
+
+
+void RubyDebuggerPart::slotRefreshBPState( const Breakpoint& BP)
+{
+ if (BP.type() == BP_TYPE_FilePos)
+ {
+ const FilePosBreakpoint& bp = dynamic_cast<const FilePosBreakpoint&>(BP);
+ if (bp.isActionDie())
+ debugger()->setBreakpoint(bp.fileName(), bp.lineNum()-1, -1, true, false);
+ else
+ debugger()->setBreakpoint(bp.fileName(), bp.lineNum()-1,
+ 1/*bp->id()*/, bp.isEnabled(), bp.isPending() );
+ }
+}
+
+
+void RubyDebuggerPart::slotStatus(const QString &msg, int state)
+{
+ QString stateIndicator;
+
+ if (state & s_dbgNotStarted)
+ {
+ stateIndicator = " ";
+ }
+ else if (state & s_appBusy)
+ {
+ stateIndicator = "A";
+ debugger()->clearExecutionPoint();
+ stateChanged( QString("active") );
+ }
+ else if (state & s_programExited)
+ {
+ stateIndicator = "E";
+ stateChanged( QString("stopped") );
+ KActionCollection *ac = actionCollection();
+ ac->action("debug_run")->setText( i18n("Restart") );
+// ac->action("debug_run")->setIcon( "1rightarrow" );
+ ac->action("debug_run")->setToolTip( i18n("Restart the program in the debugger") );
+ ac->action("debug_run")->setWhatsThis( i18n("Restart in debugger\n\n"
+ "Restarts the program in the debugger") );
+// slotStop();
+ }
+ else
+ {
+ stateIndicator = "P";
+ stateChanged( QString("paused") );
+ }
+
+ // And now? :-)
+ kdDebug(9012) << "Debugger state: " << stateIndicator << ": " << endl;
+ kdDebug(9012) << " " << msg << endl;
+
+ statusBarIndicator->setText(stateIndicator);
+ if (!msg.isEmpty())
+ mainWindow()->statusBar()->message(msg, 3000);
+}
+
+
+void RubyDebuggerPart::slotShowStep(const QString &fileName, int lineNum)
+{
+ if ( ! fileName.isEmpty() )
+ {
+ // Debugger counts lines from 1
+ debugger()->gotoExecutionPoint(KURL( fileName ), lineNum-1);
+ }
+}
+
+
+void RubyDebuggerPart::slotGotoSource(const QString &fileName, int lineNum)
+{
+ if ( ! fileName.isEmpty() )
+ partController()->editDocument(KURL( fileName ), lineNum);
+}
+
+
+void RubyDebuggerPart::slotActivePartChanged( KParts::Part* part )
+{
+ KAction* action = actionCollection()->action("debug_toggle_breakpoint");
+ if(!action)
+ return;
+
+ if(!part)
+ {
+ action->setEnabled(false);
+ return;
+ }
+ KTextEditor::ViewCursorInterface *iface
+ = dynamic_cast<KTextEditor::ViewCursorInterface*>(part->widget());
+ action->setEnabled( iface != 0 );
+}
+
+void RubyDebuggerPart::restorePartialProjectSession(const QDomElement* el)
+{
+ rdbBreakpointWidget->restorePartialProjectSession(el);
+ variableWidget->restorePartialProjectSession(el);
+}
+
+void RubyDebuggerPart::savePartialProjectSession(QDomElement* el)
+{
+ rdbBreakpointWidget->savePartialProjectSession(el);
+ variableWidget->savePartialProjectSession(el);
+}
+
+}
+
+KDevAppFrontend * RDBDebugger::RubyDebuggerPart::appFrontend( )
+{
+ return extension<KDevAppFrontend>("KDevelop/AppFrontend");
+}
+
+KDevDebugger * RDBDebugger::RubyDebuggerPart::debugger()
+{
+ return m_debugger;
+}
+
+#include "debuggerpart.moc"
diff --git a/languages/ruby/debugger/debuggerpart.h b/languages/ruby/debugger/debuggerpart.h
new file mode 100644
index 00000000..2f35df9a
--- /dev/null
+++ b/languages/ruby/debugger/debuggerpart.h
@@ -0,0 +1,110 @@
+/***************************************************************************
+ * Copyright (C) 1999-2001 by John Birch *
+ * jbb@kdevelop.org *
+ * Copyright (C) 2001 by Bernd Gehrmann *
+ * bernd@kdevelop.org *
+ * *
+ * Adapted for ruby debugging *
+ * -------------------------- *
+ * begin : Mon Nov 1 2004 *
+ * copyright : (C) 2004 by Richard Dale *
+ * email : Richard_Dale@tipitina.demon.co.uk *
+ * *
+ * 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. *
+ * *
+ ***************************************************************************/
+
+#ifndef _DEBUGGERPART_H_
+#define _DEBUGGERPART_H_
+
+#include <qguardedptr.h>
+#include "kdevplugin.h"
+#include "kdevcore.h"
+
+namespace KParts { class Part; }
+
+class QLabel;
+class QPopupMenu;
+class KDialogBase;
+class ProcessWidget;
+class ProcessLineMaker;
+class KDevAppFrontend;
+class KDevDebugger;
+
+namespace RDBDebugger
+{
+
+class RDBBreakpointWidget;
+class FramestackWidget;
+class Breakpoint;
+class DbgController;
+class DbgToolBar;
+class VariableWidget;
+class RDBOutputWidget;
+
+class RubyDebuggerPart : public KDevPlugin
+{
+ Q_OBJECT
+
+public:
+ RubyDebuggerPart( QObject *parent, const char *name, const QStringList & );
+ ~RubyDebuggerPart();
+ virtual void restorePartialProjectSession(const QDomElement* el);
+ virtual void savePartialProjectSession(QDomElement* el);
+
+private slots:
+ void guiClientAdded(KXMLGUIClient*);
+ void contextMenu(QPopupMenu *popup, const Context *context);
+ void toggleBreakpoint();
+ void contextWatch();
+ void contextRubyInspect();
+// void projectOpened();
+ void projectClosed();
+ void slotActivePartChanged(KParts::Part*);
+
+ void slotRun();
+ void slotStopDebugger();
+ void slotStop(KDevPlugin* which = 0);
+ void slotPause();
+ void slotRunToCursor();
+ void slotStepOver();
+ void slotStepInto();
+ void slotStepOut();
+
+ void slotRefreshBPState(const Breakpoint&);
+ void slotStatus(const QString &msg, int state);
+ void slotShowStep(const QString &fileName, int lineNum);
+ void slotGotoSource(const QString &fileName, int lineNum);
+
+signals:
+ void rubyInspect(const QString&);
+
+private:
+ KDevAppFrontend *appFrontend();
+ KDevDebugger *debugger();
+
+ bool startDebugger();
+ void setupController();
+
+ QGuardedPtr<VariableWidget> variableWidget;
+ QGuardedPtr<RDBBreakpointWidget> rdbBreakpointWidget;
+ QGuardedPtr<FramestackWidget> framestackWidget;
+ QGuardedPtr<RDBOutputWidget> rdbOutputWidget;
+ DbgController *controller;
+ QGuardedPtr<QLabel> statusBarIndicator;
+ QGuardedPtr<DbgToolBar> floatingToolBar;
+ ProcessLineMaker* procLineMaker;
+ ProcessLineMaker* rdbLineMaker;
+
+ QString m_contextIdent;
+ QCString m_drkonqi;
+
+ KDevDebugger *m_debugger;
+};
+
+}
+
+#endif
diff --git a/languages/ruby/debugger/framestackwidget.cpp b/languages/ruby/debugger/framestackwidget.cpp
new file mode 100644
index 00000000..836350b2
--- /dev/null
+++ b/languages/ruby/debugger/framestackwidget.cpp
@@ -0,0 +1,272 @@
+/***************************************************************************
+ begin : Sun Aug 8 1999
+ copyright : (C) 1999 by John Birch
+ email : jbb@kdevelop.org
+
+ Adapted for ruby debugging
+ --------------------------
+ begin : Mon Nov 1 2004
+ copyright : (C) 2004 by Richard Dale
+ email : Richard_Dale@tipitina.demon.co.uk
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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 "framestackwidget.h"
+#include "rdbparser.h"
+
+#include <klocale.h>
+#include <kdebug.h>
+
+#include <qheader.h>
+#include <qlistbox.h>
+#include <qregexp.h>
+#include <qstrlist.h>
+#include <qfileinfo.h>
+
+#include <ctype.h>
+
+
+/***************************************************************************/
+/***************************************************************************/
+/***************************************************************************/
+
+namespace RDBDebugger
+{
+
+FramestackWidget::FramestackWidget(QWidget *parent, const char *name, WFlags f)
+ : QListView(parent, name, f),
+ viewedThread_(0)
+{
+ setRootIsDecorated(true);
+ setSelectionMode(Single);
+ addColumn(QString());
+ setSorting(0);
+ header()->hide();
+
+ connect( this, SIGNAL(clicked(QListViewItem*)),
+ this, SLOT(slotSelectionChanged(QListViewItem*)) );
+}
+
+
+/***************************************************************************/
+
+FramestackWidget::~FramestackWidget()
+{
+}
+
+
+// **************************************************************************
+
+void FramestackWidget::clear()
+{
+ viewedThread_ = 0;
+ QListView::clear();
+}
+
+/***************************************************************************/
+
+void FramestackWidget::slotSelectionChanged(QListViewItem * item)
+{
+ if (item == 0) {
+ return;
+ }
+
+ if (item->rtti() == RTTI_THREAD_STACK_ITEM) {
+ ThreadStackItem * thread = (ThreadStackItem*) item;
+ slotSelectFrame(1, thread->threadNo());
+ } else if (item->rtti() == RTTI_FRAME_STACK_ITEM) {
+ FrameStackItem * frame = (FrameStackItem*) item;
+ slotSelectFrame(frame->frameNo(), frame->threadNo());
+ }
+
+ return;
+}
+
+/***************************************************************************/
+
+void FramestackWidget::slotSelectFrame(int frameNo, int threadNo)
+{
+ FrameStackItem * frame = findFrame(frameNo, threadNo);
+
+ if (frame != 0) {
+ setSelected(frame, true);
+ emit selectFrame(frameNo, threadNo, frame->frameName());
+ } else {
+ emit selectFrame(frameNo, threadNo, QString());
+ }
+}
+
+/***************************************************************************/
+
+void FramestackWidget::parseRDBThreadList(char *str)
+{
+ // on receipt of a thread list we must always clear the list.
+ clear();
+
+ QRegExp thread_re("(\\+)?\\s*(\\d+)\\s*(#<[^>]+>\\s*[^:]+:\\d+)");
+ int pos = thread_re.search(str);
+ viewedThread_ = 0;
+
+ while (pos != -1) {
+ ThreadStackItem* thread;
+ thread = new ThreadStackItem( this,
+ thread_re.cap(2).toInt(),
+ QString("%1 %2").arg(thread_re.cap(2)).arg(thread_re.cap(3)) );
+ // The thread with a '+' is always the viewedthread
+ if (thread_re.cap(1) == "+") {
+ viewedThread_ = thread;
+ }
+
+ pos += thread_re.matchedLength();
+ pos = thread_re.search(str, pos);
+ }
+
+ return;
+}
+
+/***************************************************************************/
+
+void FramestackWidget::parseRDBBacktraceList(char *str)
+{
+ QRegExp frame_re("#(\\d+) ([^:]+):(\\d+)(:in `([^\\n]+)')?");
+ int pos = frame_re.search(str);
+
+ while (pos != -1) {
+ QString method(frame_re.cap(5));
+ if (method == "") {
+ method = "toplevel";
+ } else {
+ method.append("(...)");
+ }
+
+ int frameNo = frame_re.cap(1).toInt();
+ QString frameName = QString("T%1#%2 %3").arg(viewedThread_->threadNo()).arg(frame_re.cap(1)).arg(method);
+ new FrameStackItem(viewedThread_, frameNo, QString(frame_re.cap(0)), frameName);
+
+ // Tell the Variable Tree that this frame is active
+ emit frameActive(frameNo, viewedThread_->threadNo(), frameName);
+
+ pos += frame_re.matchedLength();
+ pos = frame_re.search(str, pos);
+ }
+
+ if (viewedThread_ != 0) {
+ viewedThread_->setOpen(true);
+ }
+
+ return;
+}
+
+// **************************************************************************
+
+ThreadStackItem *FramestackWidget::findThread(int threadNo)
+{
+ QListViewItem *sibling = firstChild();
+ while (sibling != 0) {
+ ThreadStackItem *thread = (ThreadStackItem*) sibling;
+ if (thread->threadNo() == threadNo) {
+ return thread;
+ }
+ sibling = sibling->nextSibling();
+ }
+
+ return 0;
+}
+
+// **************************************************************************
+
+FrameStackItem *FramestackWidget::findFrame(int frameNo, int threadNo)
+{
+ ThreadStackItem * thread = findThread(threadNo);
+ if (thread == 0) {
+ kdDebug(9012) << "FramestackWidget::findFrame: no matching thread " <<
+ frameNo << " thread: " << threadNo << endl;
+ return 0; // no matching thread?
+ }
+
+ QListViewItem * frameItem = thread->firstChild();
+
+ while (frameItem != 0) {
+ if (((FrameStackItem *) frameItem)->frameNo() == frameNo) {
+ return (FrameStackItem *) frameItem;
+ }
+
+ frameItem = frameItem->nextSibling();
+ }
+
+ return 0;
+}
+
+// **************************************************************************
+// **************************************************************************
+// **************************************************************************
+
+// **************************************************************************
+
+FrameStackItem::FrameStackItem(ThreadStackItem *parent, int frameNo, const QString &frameDesc, const QString& frameName)
+ : QListViewItem(parent),
+ frameNo_(frameNo),
+ threadNo_(parent->threadNo()),
+ frameName_(frameName)
+{
+ setText(0, frameDesc);
+ key_.sprintf("%.6d", frameNo_);
+}
+
+// **************************************************************************
+
+FrameStackItem::~FrameStackItem()
+{
+}
+
+// **************************************************************************
+
+QString FrameStackItem::key(int /*column*/, bool /*ascending*/) const
+{
+
+ return key_;
+}
+
+// **************************************************************************
+// **************************************************************************
+// **************************************************************************
+
+ThreadStackItem::ThreadStackItem(FramestackWidget *parent, int threadNo, const QString &threadDesc)
+ : QListViewItem(parent),
+ threadNo_(threadNo)
+{
+ setText(0, threadDesc);
+ setExpandable(true);
+}
+
+// **************************************************************************
+
+ThreadStackItem::~ThreadStackItem()
+{
+}
+
+// **************************************************************************
+
+void ThreadStackItem::setOpen(bool open)
+{
+ if (open)
+ ((FramestackWidget*)listView())->slotSelectFrame(1, threadNo());
+
+ QListViewItem::setOpen(open);
+}
+
+}
+
+/***************************************************************************/
+/***************************************************************************/
+/***************************************************************************/
+
+#include "framestackwidget.moc"
diff --git a/languages/ruby/debugger/framestackwidget.h b/languages/ruby/debugger/framestackwidget.h
new file mode 100644
index 00000000..373d0690
--- /dev/null
+++ b/languages/ruby/debugger/framestackwidget.h
@@ -0,0 +1,115 @@
+/***************************************************************************
+ begin : Sun Aug 8 1999
+ copyright : (C) 1999 by John Birch
+ email : jbb@kdevelop.org
+
+ Adapted for ruby debugging
+ --------------------------
+ begin : Mon Nov 1 2004
+ copyright : (C) 2004 by Richard Dale
+ email : Richard_Dale@tipitina.demon.co.uk
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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. *
+ * *
+ ***************************************************************************/
+
+#ifndef _FRAMESTACKWIDGET_H_
+#define _FRAMESTACKWIDGET_H_
+
+#include <qlistview.h>
+#include <qstringlist.h>
+
+#include "rdbcontroller.h"
+
+namespace RDBDebugger
+{
+
+class FramestackWidget;
+
+
+class ThreadStackItem : public QListViewItem
+{
+public:
+ ThreadStackItem(FramestackWidget *parent, int threadNo, const QString &threadDesc);
+ virtual ~ThreadStackItem();
+
+ virtual int rtti() const { return RTTI_THREAD_STACK_ITEM; }
+
+ void setOpen(bool open);
+ int threadNo() { return threadNo_; }
+
+private:
+ int threadNo_;
+};
+
+/***************************************************************************/
+/***************************************************************************/
+/***************************************************************************/
+
+class FrameStackItem : public QListViewItem
+{
+public:
+ FrameStackItem(ThreadStackItem * parent, int frameNo, const QString & frameDesc, const QString & frameName);
+ virtual ~FrameStackItem();
+
+ virtual int rtti() const { return RTTI_FRAME_STACK_ITEM; }
+ virtual QString key(int column, bool ascending) const;
+
+ int frameNo() { return frameNo_; }
+ int threadNo() { return threadNo_; }
+ QString frameName() { return frameName_; }
+
+private:
+ int frameNo_;
+ int threadNo_;
+ QString frameName_;
+ QString key_;
+};
+
+/***************************************************************************/
+/***************************************************************************/
+/***************************************************************************/
+
+/**
+ * @author John Birch
+ */
+class FramestackWidget : public QListView
+{
+ Q_OBJECT
+
+public:
+ FramestackWidget( QWidget *parent=0, const char *name=0, WFlags f=0 );
+ virtual ~FramestackWidget();
+
+ void clear();
+
+ void parseRDBThreadList(char *str);
+ void parseRDBBacktraceList(char *str);
+
+ ThreadStackItem *findThread(int threadNo);
+ FrameStackItem *findFrame(int frameNo, int threadNo);
+
+ int viewedThread()
+ { return viewedThread_ ? viewedThread_->threadNo() : -1; }
+
+public slots:
+ void slotSelectFrame(int frameNo, int threadNo);
+ void slotSelectionChanged(QListViewItem *thisItem);
+
+signals:
+ void selectFrame(int frameNo, int threadNo, const QString& frameName);
+ void frameActive(int frameNo, int threadNo, const QString& frameName);
+
+private:
+ ThreadStackItem *viewedThread_;
+};
+
+}
+
+#endif
diff --git a/languages/ruby/debugger/hi16-action-breakpoint_add.png b/languages/ruby/debugger/hi16-action-breakpoint_add.png
new file mode 100644
index 00000000..1b41040c
--- /dev/null
+++ b/languages/ruby/debugger/hi16-action-breakpoint_add.png
Binary files differ
diff --git a/languages/ruby/debugger/hi16-action-breakpoint_delete.png b/languages/ruby/debugger/hi16-action-breakpoint_delete.png
new file mode 100644
index 00000000..c35e039f
--- /dev/null
+++ b/languages/ruby/debugger/hi16-action-breakpoint_delete.png
Binary files differ
diff --git a/languages/ruby/debugger/hi16-action-breakpoint_delete_all.png b/languages/ruby/debugger/hi16-action-breakpoint_delete_all.png
new file mode 100644
index 00000000..c35e039f
--- /dev/null
+++ b/languages/ruby/debugger/hi16-action-breakpoint_delete_all.png
Binary files differ
diff --git a/languages/ruby/debugger/hi16-action-breakpoint_edit.png b/languages/ruby/debugger/hi16-action-breakpoint_edit.png
new file mode 100644
index 00000000..ec92ced2
--- /dev/null
+++ b/languages/ruby/debugger/hi16-action-breakpoint_edit.png
Binary files differ
diff --git a/languages/ruby/debugger/kdevrbdebugger.desktop b/languages/ruby/debugger/kdevrbdebugger.desktop
new file mode 100644
index 00000000..47e59474
--- /dev/null
+++ b/languages/ruby/debugger/kdevrbdebugger.desktop
@@ -0,0 +1,73 @@
+[Desktop Entry]
+Type=Service
+Exec=blubb
+Comment=This plugin provides a frontend for the source-level debugger for Ruby.
+Comment[ca]=Aquest connector proveeix una interfície per a la depuració a nivell de codi per a Ruby.
+Comment[da]=Dette plugin sørger for en grænseflade til Ruby kildekode-fejlretteren.
+Comment[de]=Dieses Modul stellt eine Oberfläche für den Quelltext-Debugger für Ruby bereit.
+Comment[el]=Αυτό το πρόσθετο προσφέρει ένα πρόγραμμα για τον αποσφαλματωτή πηγαίου κώδικα της γλώσσας Ruby.
+Comment[es]=Este complemento proporciona un entorno para depurar código de Ruby.
+Comment[et]=See plugin pakub Ruby lähtekoodi siluri kasutajaliidest.
+Comment[eu]=Plugin honek Ruby-ren iturburu-mailako araztailearen interfaze bat bat eskeintzen du.
+Comment[fa]=این وصله، یک پایانه برای اشکال‌زدای سطح منبع رابی فراهم می‌کند.
+Comment[fr]=Ce module externe fournit une interface pour le débogueur au niveau source pour Ruby.
+Comment[gl]=Esta extensión proporciona un frontal para o depurador a nivel de código de Ruby.
+Comment[hu]=Ez a bővítőmodul grafikus felületet biztosít a Ruby forrásszintű nyomkövetőjének használatához.
+Comment[it]=Questo plugin fornisce un'interfaccia per il debugger a livello sorgente di Ruby.
+Comment[ja]=このプラグインは、Ruby のソースレベルデバッガのフロントエンドを提供します。
+Comment[nds]=Dit Moduul stellt en Böversiet för den Ruby-Borntextfehlersöker praat.
+Comment[ne]=यो प्लगइनले रूबिका लागि स्रोत-तह डिबगरका लागि फ्रेन्टइन्ड प्रदान गर्दछ ।
+Comment[nl]=Deze plugin biedt een grafische schil voor de broncode-debugger voor Ruby.
+Comment[pl]=Ta wtyczka udostępnia interfejs do debugera poziomu źródłowego dla języka Ruby.
+Comment[pt]=Este 'plugin' oferece uma interface o depurador ao nível do código para Ruby.
+Comment[pt_BR]=Este plug-in fornece uma interface para o depurador a nível de código para Ruby.
+Comment[ru]=Этот модуль предоставляет интерфейс к отладчику исходного кода для Ruby.
+Comment[sk]=Modul pre rozhranie debugera pre Ruby.
+Comment[sr]=Овај прикључак обезбеђује интерфејс за исправљач на нивоу изворног кода за Ruby.
+Comment[sr@Latn]=Ovaj priključak obezbeđuje interfejs za ispravljač na nivou izvornog koda za Ruby.
+Comment[sv]=Insticksprogrammet tillhandahåller ett gränssnitt till källkodsavlusaren för Ruby.
+Comment[tr]=Bu eklenti, Ruby için kaynak-düzeyi hata ayıklayıcıya bir önuç sağlar.
+Comment[zh_CN]=此插件提供了一个 Ruby 的源代码级调试器的前端。
+Comment[zh_TW]=此外掛程式提供 Ruby 除錯器的前端介面。
+Name=KDevRbDebugger
+Name[da]=KDevelop Ruby fejlretter
+Name[nds]=Ruby-Fehlersöök för KDevelop
+Name[pl]=KDevDebugerRb
+Name[sk]=KDev Ruby debuger
+Name[sv]=KDevelop Ruby-avlusare
+Name[zh_TW]=KDevelop Ruby 除錯器
+GenericName=Ruby Debugger Frontend
+GenericName[ca]=Interfície per al depurador Ruby
+GenericName[da]=Ruby fejlretningsgrænseflade
+GenericName[de]=Debugger-Oberfläche für Ruby
+GenericName[el]=Πρόγραμμα αποσφαλματωτή Ruby
+GenericName[es]=Interfaz para el depurador de Ruby
+GenericName[et]=Ruby siluri kasutajaliides
+GenericName[eu]=Ruby araztailearen interfazea
+GenericName[fa]=پایانۀ اشکال‌زدای رابی
+GenericName[fr]=Interface du débogueur pour Ruby
+GenericName[gl]=Frontal do depurador Ruby
+GenericName[hu]=Grafikus felület a Ruby nyomkövetőjéhez
+GenericName[it]=Interfaccia di debug per Ruby
+GenericName[ja]=Ruby デバッガフロントエンド
+GenericName[nds]=Böversiet för den Ruby-Fehlersöker
+GenericName[ne]=रूबि डिबगर फ्रेन्टइन्ड
+GenericName[nl]=Grafische schil voor de Ruby-debugger
+GenericName[pl]=Interfejs do debugera dla języka Ruby
+GenericName[pt]=Interface de Depuração Ruby
+GenericName[pt_BR]=Interface para o Depurador Ruby
+GenericName[ru]=Интегрированный отладчик Ruby
+GenericName[sk]=Ruby debuger rozhranie
+GenericName[sr]=Интерфејс исправљача за Ruby
+GenericName[sr@Latn]=Interfejs ispravljača za Ruby
+GenericName[sv]=Gränssnitt för Ruby-avlusare
+GenericName[tr]=Ruby Hata Ayıklayıcı Önucu
+GenericName[zh_CN]=Ruby 调试器前端
+GenericName[zh_TW]=Ruby 除錯器前端介面
+Icon=
+ServiceTypes=KDevelop/Plugin
+X-KDE-Library=libkdevrbdebugger
+X-KDevelop-Version=5
+X-KDevelop-Scope=Project
+X-KDevelop-ProgrammingLanguages=Ruby
+X-KDevelop-Properties=RubyDebugger
diff --git a/languages/ruby/debugger/kdevrbdebugger.rc b/languages/ruby/debugger/kdevrbdebugger.rc
new file mode 100644
index 00000000..c7e3b336
--- /dev/null
+++ b/languages/ruby/debugger/kdevrbdebugger.rc
@@ -0,0 +1,68 @@
+<!DOCTYPE kpartgui SYSTEM "kpartgui.dtd">
+<kpartgui name="KDevRbDebugger" version="10">
+<MenuBar>
+ <Menu name="debug">
+ <text>&amp;Debug</text>
+ <Action name="debug_run" group="debug"/>
+ <Action name="debug_stop" group="debug"/>
+ <Action name="debug_pause" group="debug"/>
+ <Action name="debug_runtocursor" group="debug"/>
+ <Separator group="debug"/>
+ <Action name="debug_stepover" group="debug"/>
+ <Action name="debug_stepinto" group="debug"/>
+ <Action name="debug_stepout" group="debug"/>
+ <Separator group="debug"/>
+ <Action name="debug_toggle_breakpoint" group="debug"/>
+ <Action name="debug_disable_breakpoint" group="debug"/>
+ <Separator group="debug"/>
+ </Menu>
+</MenuBar>
+<ToolBar name="debugToolBar">
+ <text>Debugger Toolbar</text>
+ <Action name="debug_run"/>
+ <Action name="debug_stepover"/>
+ <Action name="debug_stepinto"/>
+ <Action name="debug_stepout"/>
+ <WeakSeparator/>
+</ToolBar>
+<State name="stopped">
+ <enable>
+ <Action name="debug_run"/>
+ </enable>
+ <disable>
+ <Action name="debug_stop"/>
+ <Action name="debug_pause"/>
+ <Action name="debug_runtocursor"/>
+ <Action name="debug_stepover"/>
+ <Action name="debug_stepinto"/>
+ <Action name="debug_stepout"/>
+ </disable>
+</State>
+<State name="paused">
+ <enable>
+ <Action name="debug_run"/>
+ <Action name="debug_stop"/>
+ <Action name="debug_runtocursor"/>
+ <Action name="debug_stepover"/>
+ <Action name="debug_stepinto"/>
+ <Action name="debug_stepout"/>
+ </enable>
+ <disable>
+ <Action name="debug_pause"/>
+ </disable>
+</State>
+<State name="active">
+ <enable>
+ <Action name="debug_pause"/>
+ <Action name="debug_stop"/>
+ </enable>
+ <disable>
+ <Action name="debug_run"/>
+ <Action name="debug_runtocursor"/>
+ <Action name="debug_stepover"/>
+ <Action name="debug_stepinto"/>
+ <Action name="debug_stepout"/>
+ </disable>
+</State>
+</kpartgui>
+
diff --git a/languages/ruby/debugger/rdbbreakpointwidget.cpp b/languages/ruby/debugger/rdbbreakpointwidget.cpp
new file mode 100644
index 00000000..e31831de
--- /dev/null
+++ b/languages/ruby/debugger/rdbbreakpointwidget.cpp
@@ -0,0 +1,921 @@
+/***************************************************************************
+ begin : Tue May 13 2003
+ copyright : (C) 2003 by John Birch
+ email : jbb@kdevelop.org
+
+ Adapted for ruby debugging
+ --------------------------
+ begin : Mon Nov 1 2004
+ copyright : (C) 2004 by Richard Dale
+ email : Richard_Dale@tipitina.demon.co.uk
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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 "rdbbreakpointwidget.h"
+#include "rdbtable.h"
+
+#include "breakpoint.h"
+#include "domutil.h"
+
+#include <kdebug.h>
+#include <kiconloader.h>
+#include <klocale.h>
+#include <kpopupmenu.h>
+#include <kurl.h>
+
+#include <qvbuttongroup.h>
+#include <qfileinfo.h>
+#include <qheader.h>
+#include <qtable.h>
+#include <qtoolbutton.h>
+#include <qtooltip.h>
+#include <qwhatsthis.h>
+#include <qvbox.h>
+#include <qlayout.h>
+#include <qregexp.h>
+
+#include <stdlib.h>
+#include <ctype.h>
+
+/***************************************************************************/
+/***************************************************************************/
+/***************************************************************************/
+
+namespace RDBDebugger
+{
+
+enum Column {
+ Control = 0,
+ Enable = 1,
+ Type = 2,
+ Status = 3,
+ Location = 4
+};
+
+
+#define numCols 5
+
+static int m_activeFlag = 0;
+
+/***************************************************************************/
+/***************************************************************************/
+/***************************************************************************/
+
+class BreakpointTableRow : public QTableItem
+{
+public:
+
+ BreakpointTableRow(QTable* table, EditType editType, Breakpoint* bp);
+ ~BreakpointTableRow();
+
+ bool match (Breakpoint* bp) const;
+ void reset ();
+ void setRow();
+
+ Breakpoint* breakpoint() { return m_breakpoint; }
+
+private:
+ void appendEmptyRow();
+
+private:
+ Breakpoint* m_breakpoint;
+};
+
+/***************************************************************************/
+/***************************************************************************/
+/***************************************************************************/
+
+BreakpointTableRow::BreakpointTableRow(QTable* parent, EditType editType,
+ Breakpoint* bp) :
+ QTableItem(parent, editType, ""),
+ m_breakpoint(bp)
+{
+ appendEmptyRow();
+ setRow();
+}
+
+/***************************************************************************/
+
+BreakpointTableRow::~BreakpointTableRow()
+{
+ delete m_breakpoint;
+}
+
+/***************************************************************************/
+
+bool BreakpointTableRow::match(Breakpoint* breakpoint) const
+{
+ return m_breakpoint->match(breakpoint);
+}
+
+/***************************************************************************/
+
+void BreakpointTableRow::reset()
+{
+ m_breakpoint->reset();
+ setRow();
+}
+
+/***************************************************************************/
+
+void BreakpointTableRow::appendEmptyRow()
+{
+ int row = table()->numRows();
+ table()->setNumRows(row+1);
+
+ table()->setItem(row, Control, this);
+
+ QCheckTableItem* cti = new QCheckTableItem( table(), "");
+ table()->setItem(row, Enable, cti);
+}
+
+/***************************************************************************/
+
+void BreakpointTableRow::setRow()
+{
+ if ( m_breakpoint )
+ {
+ QTableItem *item = table()->item ( row(), Enable );
+ Q_ASSERT(item->rtti() == 2);
+ ((QCheckTableItem*)item)->setChecked(m_breakpoint->isEnabled());
+
+ QString status=m_breakpoint->statusDisplay(m_activeFlag);
+
+ table()->setText(row(), Status, status);
+
+ QString displayType = m_breakpoint->displayType();
+ table()->setText(row(), Location, m_breakpoint->location());
+
+ if (m_breakpoint->isTemporary())
+ displayType = i18n(" temporary");
+
+ table()->setText(row(), Type, displayType);
+ table()->adjustColumn(Type);
+ table()->adjustColumn(Status);
+ table()->adjustColumn(Location);
+ }
+}
+
+/***************************************************************************/
+/***************************************************************************/
+/***************************************************************************/
+
+RDBBreakpointWidget::RDBBreakpointWidget(QWidget *parent, const char *name) :
+ QHBox(parent, name)
+{
+ QFrame* toolbar = new QFrame( this );
+ QVBoxLayout *l = new QVBoxLayout(toolbar, 0, 0);
+
+ toolbar->setFrameStyle( QFrame::ToolBarPanel | QFrame::Plain );
+ toolbar->setLineWidth( 0 );
+
+ m_add = new QToolButton( toolbar, "add breakpoint" );
+ m_add->setPixmap ( SmallIcon ( "breakpoint_add" ) );
+ QToolTip::add ( m_add, i18n ( "Add empty breakpoint" ) + I18N_NOOP(" <Alt+A>"));
+ QWhatsThis::add( m_add, i18n("<b>Add empty breakpoint</b><p>Shows a popup menu that allows you to choose "
+ "the type of breakpoint, then adds a breakpoint of the selected type to the breakpoints list."));
+
+ m_delete = new QToolButton( toolbar, "delete breakpoint" );
+ m_delete->setPixmap ( SmallIcon ( "breakpoint_delete" ) );
+ QToolTip::add ( m_delete, i18n ( "Delete selected breakpoint" ) + I18N_NOOP(" <Delete>") );
+ QWhatsThis::add( m_delete, i18n("<b>Delete selected breakpoint</b><p>Deletes the selected breakpoint in the breakpoints list."));
+
+ m_edit = new QToolButton( toolbar, "edit breakpoint" );
+ m_edit->setPixmap ( SmallIcon ( "breakpoint_edit" ) );
+ QToolTip::add ( m_edit, i18n ( "Edit selected breakpoint" ) + I18N_NOOP(" <Return>") );
+ QWhatsThis::add( m_edit, i18n("<b>Edit selected breakpoint</b><p>Allows to edit location, condition and ignore count properties of the selected breakpoint in the breakpoints list."));
+
+ m_removeAll = new QToolButton( toolbar, "Delete all breakppoints" );
+ m_removeAll->setPixmap ( SmallIcon ( "breakpoint_delete_all" ) );
+ QToolTip::add ( m_removeAll, i18n ( "Remove all breakpoints" ) );
+ QWhatsThis::add( m_removeAll, i18n("<b>Remove all breakpoints</b><p>Removes all breakpoints in the project."));
+
+ l->addWidget(m_add);
+ l->addWidget(m_edit);
+ l->addWidget(m_delete);
+ l->addWidget(m_removeAll);
+ QSpacerItem* spacer = new QSpacerItem( 5, 5, QSizePolicy::Minimum, QSizePolicy::Expanding );
+ l->addItem(spacer);
+
+ QPopupMenu *addMenu = new QPopupMenu( this );
+ addMenu->insertItem( i18n( "File:line" ), BP_TYPE_FilePos );
+ addMenu->insertItem( i18n( "Watchpoint" ), BP_TYPE_Watchpoint );
+ addMenu->insertItem( i18n( "Catchpoint" ), BP_TYPE_Catchpoint );
+ addMenu->insertItem( i18n( "Method()" ), BP_TYPE_Function );
+ m_add->setPopup( addMenu );
+ m_add->setPopupDelay(1);
+
+ m_table = new RDBTable(0, numCols, this, name);
+ m_table->setSelectionMode(QTable::SingleRow);
+ m_table->setShowGrid (false);
+ m_table->setLeftMargin(0);
+ m_table->setFocusStyle(QTable::FollowStyle);
+
+ m_table->hideColumn(Control);
+ m_table->setColumnReadOnly(Type, true);
+ m_table->setColumnReadOnly(Status, true);
+ m_table->setColumnWidth( Enable, 20);
+
+ QHeader *header = m_table->horizontalHeader();
+
+ header->setLabel( Enable, "" );
+ header->setLabel( Type, i18n("Type") );
+ header->setLabel( Status, i18n("Status") );
+ header->setLabel( Location, i18n("Location") );
+
+ m_table->show();
+
+ m_ctxMenu = new QPopupMenu( this );
+ m_ctxMenu->insertItem( i18n( "Show" ), BW_ITEM_Show );
+ m_ctxMenu->insertItem( i18n( "Edit" ), BW_ITEM_Edit );
+ m_ctxMenu->insertItem( i18n( "Disable" ), BW_ITEM_Disable );
+ m_ctxMenu->insertItem( i18n( "Delete" ), BW_ITEM_Delete );
+
+ connect( addMenu, SIGNAL(activated(int)),
+ this, SLOT(slotAddBlankBreakpoint(int)) );
+ connect( m_delete, SIGNAL(clicked()),
+ this, SLOT(slotRemoveBreakpoint()) );
+ connect( m_edit, SIGNAL(clicked()),
+ this, SLOT(slotEditBreakpoint()) );
+ connect( m_removeAll, SIGNAL(clicked()),
+ this, SLOT(slotRemoveAllBreakpoints()) );
+
+ connect( m_table, SIGNAL(contextMenuRequested(int, int, const QPoint &)),
+ this, SLOT(slotContextMenuShow(int, int, const QPoint & )) );
+ connect( m_ctxMenu, SIGNAL(activated(int)),
+ this, SLOT(slotContextMenuSelect(int)) );
+
+ connect( m_table, SIGNAL(doubleClicked(int, int, int, const QPoint &)),
+ this, SLOT(slotRowDoubleClicked(int, int, int, const QPoint &)));
+
+ connect( m_table, SIGNAL(valueChanged(int, int)),
+ this, SLOT(slotNewValue(int, int)));
+
+ connect( m_table, SIGNAL(returnPressed()),
+ this, SLOT(slotEditBreakpoint()));
+// connect( m_table, SIGNAL(f2Pressed()),
+// this, SLOT(slotEditBreakpoint()));
+ connect( m_table, SIGNAL(deletePressed()),
+ this, SLOT(slotRemoveBreakpoint()));
+ connect( m_table, SIGNAL(insertPressed()),
+ this, SLOT(slotAddBreakpoint()));
+}
+
+/***************************************************************************/
+
+RDBBreakpointWidget::~RDBBreakpointWidget()
+{
+ delete m_table;
+}
+
+/***************************************************************************/
+
+void RDBBreakpointWidget::reset()
+{
+ for ( int row = 0; row < m_table->numRows(); row++ )
+ {
+ BreakpointTableRow* btr = (BreakpointTableRow *) m_table->item(row, Control);
+ if (btr)
+ {
+ btr->reset();
+ emit publishBPState(*(btr->breakpoint()));
+ }
+ }
+}
+
+/***************************************************************************/
+
+// When a file is loaded then we need to tell the editor (display window)
+// which lines contain a breakpoint.
+void RDBBreakpointWidget::slotRefreshBP(const KURL &filename)
+{
+ for ( int row = 0; row < m_table->numRows(); row++ )
+ {
+ BreakpointTableRow* btr = (BreakpointTableRow *) m_table->item(row, Control);
+ if (btr)
+ {
+ FilePosBreakpoint* bp = dynamic_cast<FilePosBreakpoint*>(btr->breakpoint());
+ if (bp && (bp->fileName() == filename.path()))
+ emit refreshBPState(*bp);
+ }
+ }
+}
+
+/***************************************************************************/
+
+BreakpointTableRow* RDBBreakpointWidget::find(Breakpoint *breakpoint)
+{
+ // NOTE:- The match doesn't have to be equal. Each type of bp
+ // must decide on the match criteria.
+ Q_ASSERT (breakpoint);
+
+ for ( int row = 0; row < m_table->numRows(); row++ )
+ {
+ BreakpointTableRow* btr = (BreakpointTableRow *) m_table->item(row, Control);
+ if (btr && btr->match(breakpoint))
+ return btr;
+ }
+
+ return 0;
+}
+
+/***************************************************************************/
+
+// The Id is supplied by the debugger
+BreakpointTableRow* RDBBreakpointWidget::findId(int dbgId)
+{
+ for ( int row = 0; row < m_table->numRows(); row++ )
+ {
+ BreakpointTableRow* btr = (BreakpointTableRow *) m_table->item(row, Control);
+ if (btr && btr->breakpoint()->dbgId() == dbgId)
+ return btr;
+ }
+
+ return 0;
+}
+
+/***************************************************************************/
+
+// The key is a unique number supplied by us
+BreakpointTableRow* RDBBreakpointWidget::findKey(int BPKey)
+{
+ for ( int row = 0; row < m_table->numRows(); row++ )
+ {
+ BreakpointTableRow* btr = (BreakpointTableRow *) m_table->item(row, Control);
+ if (btr && btr->breakpoint()->key() == BPKey)
+ return btr;
+ }
+
+ return 0;
+}
+
+/***************************************************************************/
+
+BreakpointTableRow* RDBBreakpointWidget::addBreakpoint(Breakpoint *bp)
+{
+ BreakpointTableRow* btr =
+ new BreakpointTableRow( m_table, QTableItem::WhenCurrent, bp );
+ emit publishBPState(*bp);
+ return btr;
+}
+
+/***************************************************************************/
+
+void RDBBreakpointWidget::removeBreakpoint(BreakpointTableRow* btr)
+{
+ if (!btr)
+ return;
+
+ // Pending but the debugger hasn't started processing this bp so
+ // we can just remove it.
+ Breakpoint* bp = btr->breakpoint();
+ if (bp->isPending() && !bp->isDbgProcessing())
+ {
+ bp->setActionDie();
+ emit publishBPState(*bp);
+ m_table->removeRow(btr->row());
+ }
+ else
+ {
+ bp->setPending(true);
+ bp->setActionClear(true);
+ emit publishBPState(*bp);
+ btr->setRow();
+ }
+}
+
+/***************************************************************************/
+
+void RDBBreakpointWidget::slotToggleBreakpoint(const QString &fileName, int lineNum)
+{
+ FilePosBreakpoint *fpBP = new FilePosBreakpoint(fileName, lineNum+1);
+
+ BreakpointTableRow* btr = find(fpBP);
+ if (btr)
+ {
+ delete fpBP;
+ removeBreakpoint(btr);
+ }
+ else
+ addBreakpoint(fpBP);
+}
+
+/***************************************************************************/
+
+void RDBBreakpointWidget::slotToggleBreakpointEnabled(const QString &fileName, int lineNum)
+{
+ FilePosBreakpoint *fpBP = new FilePosBreakpoint(fileName, lineNum+1);
+
+ BreakpointTableRow* btr = find(fpBP);
+ delete fpBP;
+ if (btr)
+ {
+ Breakpoint* bp=btr->breakpoint();
+ bp->setEnabled(!bp->isEnabled());
+ emit publishBPState(*bp);
+ }
+}
+
+/***************************************************************************/
+
+void RDBBreakpointWidget::slotToggleWatchpoint(const QString &varName)
+{
+ Watchpoint *watchpoint = new Watchpoint(varName, false, true);
+ BreakpointTableRow* btr = find(watchpoint);
+ if (btr)
+ {
+ removeBreakpoint(btr);
+ delete watchpoint;
+ }
+ else
+ addBreakpoint(watchpoint);
+}
+
+/***************************************************************************/
+
+// The debugger allows us to set pending breakpoints => do it
+void RDBBreakpointWidget::slotSetPendingBPs()
+{
+ for ( int row = 0; row < m_table->numRows(); row++ )
+ {
+ BreakpointTableRow* btr = (BreakpointTableRow *) m_table->item(row, Control);
+
+ if (btr)
+ {
+ Breakpoint* bp = btr->breakpoint();
+ if (bp->isPending() && !bp->isDbgProcessing() && bp->isValid())
+ emit publishBPState(*bp);
+ }
+ }
+}
+
+/***************************************************************************/
+
+// The debugger is having trouble with this bp - probably because a library
+// was unloaded and invalidated a bp that was previously set in the library
+// code. Reset the bp so that we can try again later.
+void RDBBreakpointWidget::slotUnableToSetBPNow(int BPid)
+{
+ if (BPid == -1)
+ reset();
+ else
+ if (BreakpointTableRow *btr = findId(BPid))
+ btr->reset();
+}
+
+/***************************************************************************/
+
+void RDBBreakpointWidget::slotParseRDBBrkptList(char *str)
+{
+ // Another example of a not too uncommon occurance
+ // No breakpoints.
+
+ // Set the new active flag so that after we have read the
+ // breakpoint list we can trim the breakpoints that have been
+ // removed (temporary breakpoints do this)
+ m_activeFlag++;
+ QRegExp breakpoint_re("(\\d+) [^:]+:\\d+");
+ int pos = 0;
+
+ pos = breakpoint_re.search(str, pos);
+ while (pos >= 0) {
+ int id = breakpoint_re.cap(1).toInt();
+
+ BreakpointTableRow* btr = findId(id);
+ if (btr)
+ {
+ Breakpoint *bp = btr->breakpoint();
+ bp->setActive(m_activeFlag, id);
+ btr->setRow();
+ emit publishBPState(*bp);
+ }
+
+ pos += breakpoint_re.matchedLength();
+ pos = breakpoint_re.search(str, pos);
+ }
+
+ str = strstr(str, "Watchpoints:");
+ if (str != 0) {
+ QRegExp watchpoint_re("(\\d+) [^\n]+\n");
+ int pos = 0;
+
+ pos = watchpoint_re.search(str, pos);
+ while (pos >= 0) {
+ int id = watchpoint_re.cap(1).toInt();
+
+ BreakpointTableRow* btr = findId(id);
+ if (btr)
+ {
+ Breakpoint *bp = btr->breakpoint();
+ bp->setActive(m_activeFlag, id);
+ btr->setRow();
+ emit publishBPState(*bp);
+ }
+
+ pos += watchpoint_re.matchedLength();
+ pos = watchpoint_re.search(str, pos);
+ }
+ }
+
+ // Remove any inactive breakpoints.
+ for ( int row = m_table->numRows()-1; row >= 0 ; row-- )
+ {
+ BreakpointTableRow* btr = (BreakpointTableRow *) m_table->item(row, Control);
+ if (btr)
+ {
+ Breakpoint* bp = btr->breakpoint();
+ if (!(bp->isActive(m_activeFlag)))
+ removeBreakpoint(btr);
+ }
+ }
+}
+
+/***************************************************************************/
+
+void RDBBreakpointWidget::slotParseRDBBreakpointSet(char *str, int BPKey)
+{
+ BreakpointTableRow* btr = findKey(BPKey);
+ if (!btr)
+ return;
+
+ Breakpoint *bp = btr->breakpoint();
+ bp->setDbgProcessing(false);
+
+ QRegExp breakpoint_re("Set breakpoint (\\d+) at [^:]+:\\d+");
+ QRegExp watchpoint_re("Set watchpoint (\\d+)");
+
+ int id = 0;
+ if (breakpoint_re.search(str, 0) != -1) {
+ id = breakpoint_re.cap(1).toInt();
+ } else if (watchpoint_re.search(str, 0) != -1) {
+ id = watchpoint_re.cap(1).toInt();
+ }
+
+ if (id > 0)
+ {
+ bp->setActive(m_activeFlag, id);
+ emit publishBPState(*bp);
+ btr->setRow();
+ }
+}
+
+/***************************************************************************/
+
+void RDBBreakpointWidget::slotAddBlankBreakpoint(int idx)
+{
+ BreakpointTableRow* btr = 0;
+ switch (idx)
+ {
+ case BP_TYPE_FilePos:
+ btr = addBreakpoint(new FilePosBreakpoint("", 0));
+ break;
+
+ case BP_TYPE_Watchpoint:
+ btr = addBreakpoint(new Watchpoint(""));
+ break;
+
+ case BP_TYPE_Catchpoint:
+ btr = addBreakpoint(new Catchpoint(""));
+ break;
+
+ case BP_TYPE_Function:
+ btr = addBreakpoint(new FunctionBreakpoint(""));
+ break;
+
+ default:
+ break;
+ }
+
+ if (btr)
+ {
+ QTableSelection ts;
+ ts.init(btr->row(), 0);
+ ts.expandTo(btr->row(), numCols );
+ m_table->addSelection(ts);
+ m_table->editCell(btr->row(), Location, false);
+ }
+}
+
+/***************************************************************************/
+
+void RDBBreakpointWidget::slotRemoveBreakpoint()
+{
+ int row = m_table->currentRow();
+ if ( row != -1)
+ {
+ BreakpointTableRow* btr = (BreakpointTableRow *) m_table->item(row, Control);
+ removeBreakpoint(btr);
+ }
+}
+
+/***************************************************************************/
+
+void RDBBreakpointWidget::slotRemoveAllBreakpoints()
+{
+ while (m_table->numRows() > 0)
+ {
+ for ( int row = m_table->numRows()-1; row>=0; row-- )
+ {
+ BreakpointTableRow* btr = (BreakpointTableRow *) m_table->item(row, Control);
+ removeBreakpoint(btr);
+ }
+ }
+}
+
+/***************************************************************************/
+
+void RDBBreakpointWidget::slotRowDoubleClicked(int row, int col, int btn, const QPoint &)
+{
+ if ( btn == Qt::LeftButton )
+ {
+// kdDebug(9012) << "in slotRowSelected row=" << row << endl;
+ BreakpointTableRow* btr = (BreakpointTableRow *) m_table->item(row, Control);
+ if (btr)
+ {
+ FilePosBreakpoint* bp = dynamic_cast<FilePosBreakpoint*>(btr->breakpoint());
+ if (bp)
+ emit gotoSourcePosition(bp->fileName(), bp->lineNum()-1);
+
+ // put the focus back on the clicked item if appropriate
+ if (col == Location)
+ m_table->editCell(row, col, false);
+ }
+ }
+}
+
+void RDBBreakpointWidget::slotContextMenuShow( int row, int /*col*/, const QPoint &mousePos )
+{
+ BreakpointTableRow *btr = (BreakpointTableRow *)m_table->item( row, Control );
+
+ if (btr != NULL)
+ {
+ m_ctxMenu->setItemEnabled( BW_ITEM_Show, (btr->breakpoint( )->type( ) == BP_TYPE_FilePos) );
+ if (btr->breakpoint( )->isEnabled( ))
+ {
+ m_ctxMenu->changeItem( BW_ITEM_Disable, i18n("Disable") );
+ }
+ else
+ {
+ m_ctxMenu->changeItem( BW_ITEM_Disable, i18n("Enable") );
+ }
+
+ //m_ctxMenu->popup( mapToGlobal( mousePos ) );
+ m_ctxMenu->popup( mousePos );
+ }
+}
+
+void RDBBreakpointWidget::slotContextMenuSelect( int item )
+{
+ int row, col;
+ BreakpointTableRow *btr;
+ Breakpoint *bp;
+ FilePosBreakpoint *fbp;
+
+ row= m_table->currentRow( );
+ if (row == -1)
+ return;
+ btr = (BreakpointTableRow *)m_table->item( row, Control );
+ if (btr == NULL)
+ return;
+ bp = btr->breakpoint( );
+ if (bp == NULL)
+ return;
+ fbp = dynamic_cast<FilePosBreakpoint*>(bp);
+
+ switch( item )
+ {
+ case BW_ITEM_Show:
+ if (fbp)
+ emit gotoSourcePosition(fbp->fileName(), fbp->lineNum()-1);
+ break;
+ case BW_ITEM_Edit:
+ col = m_table->currentColumn( );
+ if (col == Location)
+ m_table->editCell(row, col, false);
+ break;
+ case BW_ITEM_Disable:
+ bp->setEnabled( !bp->isEnabled( ) );
+ btr->setRow( );
+ emit publishBPState( *bp );
+ break;
+ case BW_ITEM_Delete:
+ slotRemoveBreakpoint( );
+ break;
+ default:
+ // oops, check it out! this case is not in sync with the
+ // m_ctxMenu. Check the enum in the header file.
+ return;
+ }
+}
+
+/***************************************************************************/
+
+void RDBBreakpointWidget::slotEditRow(int row, int col, const QPoint &)
+{
+// kdDebug(9012) << "in slotEditRow row=" << row << endl;
+ BreakpointTableRow* btr = (BreakpointTableRow *) m_table->item(row, Control);
+ if (btr)
+ {
+ if (col == Location)
+ m_table->editCell(row, col, false);
+ }
+}
+
+/***************************************************************************/
+
+void RDBBreakpointWidget::slotNewValue(int row, int col)
+{
+// kdDebug(9012) << "in slotNewValue row=" << row << endl;
+ BreakpointTableRow* btr = (BreakpointTableRow *) m_table->item(row, Control);
+
+ if (btr)
+ {
+ bool changed=false;
+ Breakpoint* bp = btr->breakpoint();
+ switch (col)
+ {
+
+ case Enable:
+ {
+ QCheckTableItem *item = (QCheckTableItem*)m_table->item ( row, Enable );
+ if ( item->isChecked() != bp->isEnabled() )
+ {
+ bp->setEnabled(item->isChecked());
+ bp->setPending(true);
+ bp->setActionModify(true);
+ changed = true;
+ }
+ break;
+ }
+
+ case Location:
+ {
+ if (bp->location() != m_table->text(btr->row(), Location))
+ {
+// kdDebug(9012) << "Old location [" << bp->location() << "]" << endl;
+// kdDebug(9012) << "New location [" << m_table->text(btr->row(), Location) << "]" << endl;
+ bp->setActionDie();
+ emit publishBPState(*bp);
+ bp->setPending(true);
+ bp->setActionAdd(true);
+ bp->setLocation(m_table->text(btr->row(), Location));
+ changed = true;
+ }
+ break;
+ }
+
+ case Type:
+ case Status:
+ default:
+ break;
+ }
+
+ if (changed)
+ {
+ btr->setRow();
+ emit publishBPState(*bp);
+ }
+ }
+}
+
+/***************************************************************************/
+
+void RDBBreakpointWidget::slotEditBreakpoint(const QString &fileName, int lineNum)
+{
+ FilePosBreakpoint *fpBP = new FilePosBreakpoint(fileName, lineNum+1);
+
+ BreakpointTableRow* btr = find(fpBP);
+ delete fpBP;
+
+ if (btr)
+ {
+ QTableSelection ts;
+ ts.init(btr->row(), 0);
+ ts.expandTo(btr->row(), numCols);
+ m_table->addSelection(ts);
+ m_table->editCell(btr->row(), Location, false);
+ }
+
+}
+
+/***************************************************************************/
+
+void RDBBreakpointWidget::slotEditBreakpoint()
+{
+ m_table->editCell(m_table->currentRow(), Location, false);
+}
+
+/***************************************************************************/
+
+void RDBBreakpointWidget::savePartialProjectSession(QDomElement* el)
+{
+ QDomDocument domDoc = el->ownerDocument();
+ if (domDoc.isNull())
+ return;
+
+ QDomElement breakpointListEl = domDoc.createElement("breakpointList");
+ for ( int row = 0; row < m_table->numRows(); row++ )
+ {
+ BreakpointTableRow* btr =
+ (BreakpointTableRow *) m_table->item(row, Control);
+ Breakpoint* bp = btr->breakpoint();
+
+ QDomElement breakpointEl =
+ domDoc.createElement("breakpoint"+QString::number(row));
+
+ breakpointEl.setAttribute("type", bp->type());
+ breakpointEl.setAttribute("location", bp->location(false));
+ breakpointEl.setAttribute("enabled", bp->isEnabled());
+
+ breakpointListEl.appendChild(breakpointEl);
+ }
+
+ if (!breakpointListEl.isNull())
+ el->appendChild(breakpointListEl);
+}
+
+/***************************************************************************/
+
+void RDBBreakpointWidget::restorePartialProjectSession(const QDomElement* el)
+{
+ QDomElement breakpointListEl = el->namedItem("breakpointList").toElement();
+ if (!breakpointListEl.isNull())
+ {
+ QDomElement breakpointEl;
+ for (breakpointEl = breakpointListEl.firstChild().toElement();
+ !breakpointEl.isNull();
+ breakpointEl = breakpointEl.nextSibling().toElement())
+ {
+ Breakpoint* bp=0;
+ BP_TYPES type = (BP_TYPES) breakpointEl.attribute( "type", "0").toInt();
+ switch (type)
+ {
+ case BP_TYPE_FilePos:
+ {
+ bp = new FilePosBreakpoint("", 0);
+ break;
+ }
+ case BP_TYPE_Watchpoint:
+ {
+ bp = new Watchpoint("");
+ break;
+ }
+ case BP_TYPE_Catchpoint:
+ {
+ bp = new Catchpoint("");
+ break;
+ }
+ case BP_TYPE_Function:
+ {
+ bp = new FunctionBreakpoint("");
+ break;
+ }
+ default:
+ break;
+ }
+
+ // Common settings for any type of breakpoint
+ if (bp)
+ {
+ bp->setLocation(breakpointEl.attribute( "location", ""));
+ bp->setEnabled(breakpointEl.attribute( "enabled", "1").toInt());
+
+ // Add the bp if we don't already have it.
+ if (!find(bp))
+ addBreakpoint(bp);
+ else
+ delete bp;
+ }
+ }
+ }
+}
+
+/***************************************************************************/
+
+void RDBBreakpointWidget::slotAddBreakpoint( )
+{
+ if (m_add->popup())
+ {
+ m_add->popup()->popup(mapToGlobal(this->geometry().topLeft()));
+ }
+}
+
+/***************************************************************************/
+
+void RDBBreakpointWidget::focusInEvent( QFocusEvent */* e*/ )
+{
+ m_table->setFocus();
+}
+
+}
+
+
+#include "rdbbreakpointwidget.moc"
diff --git a/languages/ruby/debugger/rdbbreakpointwidget.h b/languages/ruby/debugger/rdbbreakpointwidget.h
new file mode 100644
index 00000000..65e6e15b
--- /dev/null
+++ b/languages/ruby/debugger/rdbbreakpointwidget.h
@@ -0,0 +1,120 @@
+/***************************************************************************
+ begin : Tue May 13 2003
+ copyright : (C) 2003 by John Birch
+ email : jbb@kdevelop.org
+
+ Adapted for ruby debugging
+ --------------------------
+ begin : Mon Nov 1 2004
+ copyright : (C) 2004 by Richard Dale
+ email : Richard_Dale@tipitina.demon.co.uk
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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. *
+ * *
+ ***************************************************************************/
+
+#ifndef _RDBBreakpointWidget_H_
+#define _RDBBreakpointWidget_H_
+
+#include <qhbox.h>
+#include <qpopupmenu.h>
+
+class QDomElement;
+class QToolButton;
+class KURL;
+
+/***************************************************************************/
+/***************************************************************************/
+/***************************************************************************/
+
+namespace RDBDebugger
+{
+class Breakpoint;
+class BreakpointTableRow;
+class RDBTable;
+
+class RDBBreakpointWidget : public QHBox
+{
+ Q_OBJECT
+
+public:
+ RDBBreakpointWidget( QWidget* parent=0, const char* name=0 );
+ virtual ~RDBBreakpointWidget();
+
+ void reset();
+
+ void savePartialProjectSession(QDomElement* el);
+ void restorePartialProjectSession(const QDomElement* el);
+
+
+public slots:
+ // Connected to from the editor widget:
+ void slotToggleBreakpoint(const QString &filename, int lineNum);
+ void slotToggleBreakpointEnabled(const QString &fileName, int lineNum);
+
+ // Connected to from the variable widget:
+ void slotToggleWatchpoint(const QString &varName);
+
+ // Connected to from the dbgcontroller:
+ void slotSetPendingBPs();
+ void slotUnableToSetBPNow(int BPNo);
+ void slotParseRDBBrkptList(char *str);
+ void slotParseRDBBreakpointSet(char *str, int BPKey);
+
+ void slotRefreshBP(const KURL &filename);
+
+protected:
+ enum BW_ITEMS { BW_ITEM_Show, BW_ITEM_Edit, BW_ITEM_Disable, BW_ITEM_Delete };
+ virtual void focusInEvent(QFocusEvent *e);
+
+private slots:
+ void slotRemoveBreakpoint();
+ void slotRemoveAllBreakpoints();
+ void slotEditBreakpoint(const QString &fileName, int lineNum);
+ void slotEditBreakpoint();
+ void slotAddBreakpoint();
+ void slotAddBlankBreakpoint(int idx);
+ void slotRowDoubleClicked(int row, int col, int button, const QPoint & mousePos);
+ void slotContextMenuShow( int row, int col, const QPoint &mousePos );
+ void slotContextMenuSelect( int item );
+ void slotEditRow(int row, int col, const QPoint & mousePos);
+ void slotNewValue(int row, int col);
+
+signals:
+ void publishBPState(const Breakpoint& brkpt);
+ void refreshBPState(const Breakpoint& brkpt);
+ void gotoSourcePosition(const QString &fileName, int lineNum);
+ void clearAllBreakpoints();
+
+private:
+ BreakpointTableRow* find(Breakpoint *bp);
+ BreakpointTableRow* findId(int id);
+ BreakpointTableRow* findKey(int BPKey);
+
+ void setActive();
+ BreakpointTableRow* addBreakpoint(Breakpoint *bp);
+ void removeBreakpoint(BreakpointTableRow* btr);
+
+private:
+ RDBTable* m_table;
+
+ QToolButton* m_add;
+ QToolButton* m_delete;
+ QToolButton* m_edit;
+ QToolButton* m_removeAll;
+ QPopupMenu* m_ctxMenu;
+};
+
+/***************************************************************************/
+/***************************************************************************/
+/***************************************************************************/
+
+}
+
+#endif
diff --git a/languages/ruby/debugger/rdbcommand.cpp b/languages/ruby/debugger/rdbcommand.cpp
new file mode 100644
index 00000000..98dc3a80
--- /dev/null
+++ b/languages/ruby/debugger/rdbcommand.cpp
@@ -0,0 +1,81 @@
+/***************************************************************************
+ begin : Sun Aug 8 1999
+ copyright : (C) 1999 by John Birch
+ email : jbb@kdevelop.org
+
+ Adapted for ruby debugging
+ --------------------------
+ begin : Mon Nov 1 2004
+ copyright : (C) 2004 by Richard Dale
+ email : Richard_Dale@tipitina.demon.co.uk
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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 "rdbcommand.h"
+#include "breakpoint.h"
+#include "variablewidget.h"
+
+namespace RDBDebugger
+{
+
+
+RDBCommand::RDBCommand(const QCString &setCommand, bool isRunCmd, bool isInfoCmd)
+ : DbgCommand(setCommand, isRunCmd, isInfoCmd)
+{
+// if (prompt_) {
+// cmdBuffer_ = QCString().sprintf("set prompt %c%c\n", BLOCK_START, prompt_) +
+// command_ +
+// idlePrompt_;
+// }
+}
+
+/***************************************************************************/
+
+RDBCommand::~RDBCommand()
+{
+}
+
+/***************************************************************************/
+/***************************************************************************/
+/***************************************************************************/
+
+RDBItemCommand::RDBItemCommand( VarItem *item,
+ const QCString &command,
+ bool isRunCmd)
+ : RDBCommand(command, isRunCmd, true),
+ item_(item)
+{
+}
+
+/***************************************************************************/
+
+RDBItemCommand::~RDBItemCommand()
+{
+}
+
+
+RDBSetBreakpointCommand::RDBSetBreakpointCommand(const QCString &command, int key)
+ : RDBCommand(command, false, false),
+ key_(key)
+{
+}
+
+/***************************************************************************/
+
+RDBSetBreakpointCommand::~RDBSetBreakpointCommand()
+{
+}
+
+/***************************************************************************/
+/***************************************************************************/
+/***************************************************************************/
+
+}
diff --git a/languages/ruby/debugger/rdbcommand.h b/languages/ruby/debugger/rdbcommand.h
new file mode 100644
index 00000000..663c8119
--- /dev/null
+++ b/languages/ruby/debugger/rdbcommand.h
@@ -0,0 +1,99 @@
+/***************************************************************************
+ begin : Sun Aug 8 1999
+ copyright : (C) 1999 by John Birch
+ email : jbb@kdevelop.org
+
+ Adapted for ruby debugging
+ --------------------------
+ begin : Mon Nov 1 2004
+ copyright : (C) 2004 by Richard Dale
+ email : Richard_Dale@tipitina.demon.co.uk
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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. *
+ * *
+ ***************************************************************************/
+
+#ifndef _RDBCOMMAND_H_
+#define _RDBCOMMAND_H_
+
+#include "dbgcommand.h"
+
+namespace RDBDebugger
+{
+
+class Breakpoint;
+class VarItem;
+
+// sigh - namespace's don't work on some of the older compilers
+enum RDBCmd
+{
+ CONSTANTS = 'C',
+ CVARS = 'V',
+ IVARS = 'I',
+ LOCALS = 'L'
+};
+
+#define RUNCMD (true)
+#define NOTRUNCMD (false)
+#define INFOCMD (true)
+#define NOTINFOCMD (false)
+
+/**
+ * @author John Birch
+ */
+
+class RDBCommand : public DbgCommand
+{
+public:
+ RDBCommand(const QCString& command, bool isRunCmd=false, bool isInfoCmd=true);
+ virtual ~RDBCommand();
+
+private:
+ static QCString idlePrompt_;
+};
+
+/***************************************************************************/
+/***************************************************************************/
+/***************************************************************************/
+class RDBItemCommand : public RDBCommand
+{
+public:
+ RDBItemCommand(VarItem *item, const QCString &command,
+ bool isRunCmd=false);
+ virtual ~RDBItemCommand();
+
+ VarItem *getItem() { return item_; }
+
+private:
+ VarItem *item_;
+};
+
+
+/***************************************************************************/
+/***************************************************************************/
+/***************************************************************************/
+class RDBSetBreakpointCommand : public RDBCommand
+{
+public:
+ RDBSetBreakpointCommand(const QCString& setCommand, int key);
+ virtual ~RDBSetBreakpointCommand();
+
+ int getKey() const { return key_; }
+
+private:
+ int key_;
+};
+
+/***************************************************************************/
+/***************************************************************************/
+/***************************************************************************/
+
+}
+
+#endif
diff --git a/languages/ruby/debugger/rdbcontroller.cpp b/languages/ruby/debugger/rdbcontroller.cpp
new file mode 100644
index 00000000..160754a0
--- /dev/null
+++ b/languages/ruby/debugger/rdbcontroller.cpp
@@ -0,0 +1,1414 @@
+// *************************************************************************
+// rdbcontroller.cpp - description
+// -------------------
+// begin : Sun Aug 8 1999
+// copyright : (C) 1999 by John Birch
+// email : jbb@kdevelop.org
+//
+// Adapted for ruby debugging
+// --------------------------
+// begin : Mon Nov 1 2004
+// copyright : (C) 2004 by Richard Dale
+// email : Richard_Dale@tipitina.demon.co.uk
+// **************************************************************************
+//
+// **************************************************************************
+// * *
+// * 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 "rdbcontroller.h"
+
+#include <sys/types.h>
+#include <fcntl.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <errno.h>
+
+#include "breakpoint.h"
+#include "framestackwidget.h"
+#include "rdbcommand.h"
+#include "stty.h"
+#include "variablewidget.h"
+#include "domutil.h"
+#include "settings.h"
+
+#include <kapplication.h>
+#include <kconfig.h>
+#include <kdebug.h>
+#include <kglobal.h>
+#include <klocale.h>
+#include <kmessagebox.h>
+#include <kprocess.h>
+
+#include <qdatetime.h>
+#include <qfileinfo.h>
+#include <qregexp.h>
+#include <qstring.h>
+#include <qtextstream.h>
+
+#include <iostream>
+#include <ctype.h>
+#include <stdlib.h>
+using namespace std;
+
+// **************************************************************************
+//
+// Does all the communication between rdb and the kdevelop's debugger code.
+// Significatant classes being used here are
+//
+// RDBParser - parses the "variable" data using the vartree and varitems
+// VarTree - where the variable data will end up
+// FrameStack - tracks the program frames and allows the user to switch between
+// and therefore view the calling funtions and their data
+// Breakpoint - Where and what to do with breakpoints.
+// STTY - the tty that the _application_ will run on.
+//
+// Significant variables
+// state_ - be very careful setting this. The controller is totally
+// dependent on this reflecting the correct state. For instance,
+// if the app is busy but we don't think so, then we lose control
+// of the app. The only way to get out of these situations is to
+// delete (stop) the controller.
+// currentFrame_
+// - Holds the frame number where and locals/variable information will
+// go to
+//
+//
+// **************************************************************************
+
+namespace RDBDebugger
+{
+
+// This is here so we can check for startup /shutdown problems
+int debug_controllerExists = false;
+
+// At the moment a Unix domain socket is used. It might be better to
+// change to tcp/ip and listen on a port instead
+QCString RDBController::unixSocketPath_;
+
+
+RDBController::RDBController(VariableTree *varTree, FramestackWidget *frameStack, QDomDocument &projectDom)
+ : DbgController(),
+ frameStack_(frameStack),
+ varTree_(varTree),
+ currentFrame_(1),
+ viewedThread_(-1),
+ stdoutOutputLen_(0),
+ stdoutOutput_(new char[4096]),
+ holdingZone_(),
+ rdbOutputLen_(0),
+ rdbOutput_(new char[49152]),
+ socketNotifier_(0),
+ currentCmd_(0),
+ currentPrompt_("(rdb:1) "),
+ tty_(0),
+ state_(s_dbgNotStarted|s_appNotStarted|s_silent),
+ programHasExited_(false),
+ dom(projectDom),
+ config_forceBPSet_(true),
+ config_dbgTerminal_(false)
+{
+ struct sockaddr_un sockaddr;
+ unixSocketPath_.sprintf("/tmp/.rubydebugger%d", getpid());
+ QFileInfo unixSocket(unixSocketPath_);
+
+ stdoutSizeofBuf_ = sizeof(stdoutOutput_);
+ rdbSizeofBuf_ = sizeof(rdbOutput_);
+
+ if (unixSocket.exists()) {
+ unlink(unixSocketPath_);
+ }
+
+ masterSocket_ = socket(AF_UNIX, SOCK_STREAM, 0);
+ sockaddr.sun_family = AF_UNIX;
+ strcpy(sockaddr.sun_path, unixSocketPath_);
+ bind(masterSocket_, (const struct sockaddr*) &sockaddr, sizeof(sockaddr));
+ listen(masterSocket_, 1);
+ acceptNotifier_ = new QSocketNotifier(masterSocket_, QSocketNotifier::Read, this);
+ QObject::connect( acceptNotifier_, SIGNAL(activated(int)),
+ this, SLOT(slotAcceptConnection(int)) );
+
+ configure();
+ cmdList_.setAutoDelete(true);
+
+ Q_ASSERT(! debug_controllerExists);
+ debug_controllerExists = true;
+}
+
+// **************************************************************************
+
+// Deleting the controller involves shutting down rdb nicely.
+// When were attached to a process, we must first detach so that the process
+// can continue running as it was before being attached. rdb is quite slow to
+// detach from a process, so we must process events within here to get a "clean"
+// shutdown.
+RDBController::~RDBController()
+{
+ delete[] stdoutOutput_;
+ delete[] rdbOutput_;
+ debug_controllerExists = false;
+
+ QFileInfo unixSocket(unixSocketPath_);
+ if (unixSocket.exists()) {
+ unlink(unixSocketPath_);
+ }
+}
+
+// **************************************************************************
+
+void RDBController::configure()
+{
+}
+
+// **************************************************************************
+
+// Fairly obvious that we'll add whatever command you give me to a queue
+// If you tell me to, I'll put it at the head of the queue so it'll run ASAP
+// Not quite so obvious though is that if we are going to run again. then any
+// information requests become redundent and must be removed.
+// We also try and run whatever command happens to be at the head of
+// the queue.
+void RDBController::queueCmd(DbgCommand *cmd, bool executeNext)
+{
+ // We remove any info command or _run_ command if we are about to
+ // add a run command.
+ if (cmd->isARunCmd())
+ removeInfoRequests();
+
+ if (executeNext)
+ cmdList_.insert(0, cmd);
+ else
+ cmdList_.append (cmd);
+}
+
+// **************************************************************************
+
+// If the appliction can accept a command and we've got one waiting
+// then send it.
+// Commands can be just request for data (or change rdbs state in someway)
+// or they can be "run" commands. If a command is sent to rdb our internal
+// state will get updated.
+void RDBController::executeCmd()
+{
+ if (stateIsOn(s_dbgNotStarted|s_waitForWrite|s_appBusy|s_shuttingDown) || !dbgProcess_)
+ return;
+
+ if (currentCmd_ == 0) {
+ if (cmdList_.isEmpty())
+ return;
+
+ currentCmd_ = cmdList_.take(0);
+ }
+
+ if (!currentCmd_->moreToSend()) {
+ delete currentCmd_;
+ if (cmdList_.isEmpty()) {
+ currentCmd_ = 0;
+ return;
+ }
+
+ currentCmd_ = cmdList_.take(0);
+ }
+
+ char * ptr = currentCmd_->cmdToSend().data();
+ int bytesToWrite = currentCmd_->cmdLength();
+ int bytesWritten = 0;
+
+ while (bytesToWrite > 0) {
+ bytesWritten = write(socket_, ptr, bytesToWrite);
+ bytesToWrite -= bytesWritten;
+ ptr += bytesWritten;
+ }
+
+ if (currentCmd_->isARunCmd()) {
+ setStateOn(s_appBusy);
+ kdDebug(9012) << "App is busy" << endl;
+ setStateOff(s_appNotStarted|s_programExited|s_silent);
+ }
+
+ QString prettyCmd = currentCmd_->cmdToSend();
+ prettyCmd = currentPrompt_ + prettyCmd;
+ emit rdbStdout( prettyCmd.latin1() );
+
+ if (!stateIsOn(s_silent))
+ emit dbgStatus("", state_);
+}
+
+// **************************************************************************
+
+void RDBController::destroyCmds()
+{
+ if (currentCmd_)
+ {
+ delete currentCmd_;
+ currentCmd_ = 0;
+ }
+
+ while (!cmdList_.isEmpty())
+ delete cmdList_.take(0);
+}
+
+// **********************************************************************
+
+void RDBController::removeInfoRequests()
+{
+ int i = cmdList_.count();
+ while (i)
+ {
+ i--;
+ DbgCommand *cmd = cmdList_.at(i);
+ if (cmd->isAnInfoCmd() || cmd->isARunCmd())
+ delete cmdList_.take(i);
+ }
+}
+
+// **********************************************************************
+
+// Pausing an app removes any pending run commands so that the app doesn't
+// start again. If we want to be silent then we remove any pending info
+// commands as well.
+void RDBController::pauseApp()
+{
+ int i = cmdList_.count();
+ while (i)
+ {
+ i--;
+ DbgCommand *cmd = cmdList_.at(i);
+ if ((stateIsOn(s_silent) && cmd->isAnInfoCmd()) || cmd->isARunCmd())
+ delete cmdList_.take(i);
+ }
+
+ if (dbgProcess_ && stateIsOn(s_appBusy))
+ dbgProcess_->kill(SIGINT);
+}
+
+// **********************************************************************
+
+// Whenever the program pauses we need to refresh the data visible to
+// the user. The reason we've stopped may be passed in to be emitted.
+void RDBController::actOnProgramPause(const QString &msg)
+{
+ // We're only stopping if we were running, of course.
+ if (stateIsOn(s_appBusy))
+ {
+ kdDebug(9012) << "App is paused" << endl;
+ setStateOff(s_appBusy);
+ if (stateIsOn(s_silent))
+ return;
+
+ emit dbgStatus (msg, state_);
+
+ // We're always at frame one when the program stops
+ // and we must reset the active flag
+ currentFrame_ = 1;
+ varTree_->nextActivationId();
+ setStateOn(s_fetchLocals);
+
+ queueCmd(new RDBCommand("where", NOTRUNCMD, INFOCMD), true);
+ queueCmd(new RDBCommand("thread list", NOTRUNCMD, INFOCMD), true);
+
+ if (stateIsOn(s_fetchGlobals)) {
+ queueCmd(new RDBCommand("var global", NOTRUNCMD, INFOCMD));
+ }
+
+ emit acceptPendingBPs();
+ }
+}
+
+// **************************************************************************
+
+// There is no app anymore. This can be caused by program exiting
+// an invalid program specified or ...
+// rdb is still running though, but only the run command (may) make sense
+// all other commands are disabled.
+void RDBController::programNoApp(const QString &msg, bool msgBox)
+{
+ state_ = (s_appNotStarted|s_programExited|(state_&(s_shuttingDown)));
+ destroyCmds();
+
+ // We're always at frame one when the program stops
+ // and we must reset the active flag
+ viewedThread_ = -1;
+ currentFrame_ = 1;
+ varTree_->nextActivationId();
+
+ // Now wipe the tree out
+ varTree_->viewport()->setUpdatesEnabled(false);
+ varTree_->prune();
+ varTree_->viewport()->setUpdatesEnabled(true);
+ varTree_->repaint();
+
+ frameStack_->clear();
+
+ if (msgBox)
+ KMessageBox::error(0, i18n("rdb message:\n")+msg);
+
+ emit dbgStatus (msg, state_);
+}
+
+// **************************************************************************
+
+// The program location falls out of rdb. We treat
+// it as a wrapped command.
+// The data gets parsed here and emitted in its component parts.
+void RDBController::parseProgramLocation(char *buf)
+{
+ QString buffer(buf);
+ QString line;
+ QTextStream input(&buffer, IO_ReadOnly);
+ QString sourceFile;
+ int sourceLine = 0;
+
+ // "1: a = 1"
+ QRegExp display_re("^(\\d+):\\s(.*)$");
+
+ // "/opt/qt/src/widgets/qlistview.rb:1558:puts 'hello world'"
+ QRegExp sourcepos_re("^([^:]+):(\\d+):");
+
+ line = input.readLine();
+ while (! line.isNull()) {
+ if (sourcepos_re.search(line, 0) >= 0) {
+ sourceFile = sourcepos_re.cap(1);
+ sourceLine = sourcepos_re.cap(2).toInt();
+ } else if (display_re.search(line, 0) >= 0) {
+ varTree_->watchRoot()->updateWatchExpression(display_re.cap(1).toInt(), display_re.cap(2));
+ }
+
+ line = input.readLine();
+ }
+
+ if ( !sourceFile.isNull()
+ && ( traceIntoRuby_
+ || ( !sourceFile.endsWith("/qtruby.rb")
+ && !sourceFile.endsWith("/korundum.rb") ) )
+ && !sourceFile.endsWith("/debuggee.rb") )
+ {
+ actOnProgramPause(QString());
+ emit showStepInSource(sourceFile, sourceLine, "");
+ return;
+ }
+
+ if (stateIsOn(s_appBusy))
+ actOnProgramPause(i18n("No source: %1").arg(sourceFile));
+ else
+ emit dbgStatus (i18n("No source: %1").arg(sourceFile), state_);
+}
+
+// **************************************************************************
+
+// parsing the backtrace list will cause the vartree to be refreshed
+void RDBController::parseBacktraceList(char *buf)
+{
+ frameStack_->parseRDBBacktraceList(buf);
+}
+
+// **************************************************************************
+
+void RDBController::parseThreadList(char *buf)
+{
+ frameStack_->parseRDBThreadList(buf);
+ viewedThread_ = frameStack_->viewedThread();
+ varTree_->setCurrentThread(viewedThread_);
+}
+
+// **************************************************************************
+
+void RDBController::parseSwitchThread(char *buf)
+{
+ // Look for the thread number
+ // 2 #<Thread:0x30091998 sleep> /home/duke/play/testit/trykorundum/src/bar.rb:13
+ QRegExp thread_re("(\\d+)");
+ if (thread_re.search(buf) != -1) {
+ viewedThread_ = thread_re.cap(1).toInt();
+ currentFrame_ = 1;
+ }
+}
+
+// **************************************************************************
+
+// After an 'up nnn' or 'down nnn' command, get the new source file and line no.
+void RDBController::parseFrameMove(char *buf)
+{
+ QString sourceFile;
+ int sourceLine = 0;
+
+ if (stateIsOn(s_fetchLocals)) {
+ return;
+ }
+
+ // "#2 /home/duke/play/testit/trykorundum/src/main.rb:11"
+ QRegExp sourcepos_re("#\\d+\\s([^:]+):(\\d+)");
+ if (sourcepos_re.search(buf) != -1) {
+ sourceFile = sourcepos_re.cap(1);
+ sourceLine = sourcepos_re.cap(2).toInt();
+
+ if ( !sourceFile.isNull()
+ && ( traceIntoRuby_
+ || ( !sourceFile.endsWith("/qtruby.rb")
+ && !sourceFile.endsWith("/korundum.rb") ) )
+ && !sourceFile.endsWith("/debuggee.rb") )
+ {
+ emit showStepInSource(sourceFile, sourceLine, "");
+ return;
+ }
+ }
+
+ emit dbgStatus(i18n("No source: %1").arg(sourceFile), state_);
+}
+
+// **************************************************************************
+
+// When a breakpoint has been set, rdb responds with some data about the
+// new breakpoint. We just inform the breakpoint system about this.
+void RDBController::parseBreakpointSet(char *buf)
+{
+ if (RDBSetBreakpointCommand *BPCmd = dynamic_cast<RDBSetBreakpointCommand*>(currentCmd_))
+ {
+ // ... except in this case :-) A -1 key tells us that this is
+ // a special internal breakpoint, and we shouldn't do anything
+ // with it. Currently there are _no_ internal breakpoints.
+ if (BPCmd->getKey() != -1) {
+ emit rawRDBBreakpointSet(buf, BPCmd->getKey());
+ }
+ }
+}
+
+// **************************************************************************
+
+// Extra data needed by an item was requested. Here's the result.
+// If it's an ordinary 'p ' command then just echo the result on
+// the RDB console and don't bother parsing.
+void RDBController::parseRequestedData(char *buf)
+{
+ if (RDBItemCommand *rdbItemCommand = dynamic_cast<RDBItemCommand*> (currentCmd_))
+ {
+ // Fish out the item from the command and let it deal with the data
+ VarItem *item = rdbItemCommand->getItem();
+ varTree_->viewport()->setUpdatesEnabled(false);
+ item->expandValue(buf);
+ varTree_->viewport()->setUpdatesEnabled(true);
+ varTree_->repaint();
+ }
+}
+
+
+// **************************************************************************
+
+// Select a different frame to view. We need to get and (maybe) display
+// where we are in the program source.
+void RDBController::parseFrameSelected(char *buf)
+{
+ if (!stateIsOn(s_silent)) {
+ emit showStepInSource("", -1, "");
+ emit dbgStatus (i18n("No source: %1").arg(QString(buf)), state_);
+ }
+}
+
+// **************************************************************************
+
+// Sets the id of the display in the VarTree and a current value.
+void RDBController::parseDisplay(char *buf, char * expr)
+{
+ varTree_->viewport()->setUpdatesEnabled(false);
+ varTree_->watchRoot()->setWatchExpression(buf, expr);
+ varTree_->viewport()->setUpdatesEnabled(true);
+ varTree_->repaint();
+}
+
+// **************************************************************************
+
+// Updates the watch expressions with current values
+void RDBController::parseUpdateDisplay(char *buf)
+{
+ varTree_->viewport()->setUpdatesEnabled(false);
+
+ QRegExp display_re("(\\d+):\\s([^\n]*)\n");
+
+ int pos = display_re.search(buf);
+ while (pos != -1) {
+ varTree_->watchRoot()->updateWatchExpression(display_re.cap(1).toInt(), display_re.cap(2));
+
+ pos += display_re.matchedLength();
+ pos = display_re.search(buf, pos);
+ }
+
+ varTree_->viewport()->setUpdatesEnabled(true);
+ varTree_->repaint();
+}
+
+// **************************************************************************
+
+// This is called on program stop to process the globals.
+void RDBController::parseGlobals(char *buf)
+{
+ varTree_->viewport()->setUpdatesEnabled(false);
+ varTree_->globalRoot()->setGlobals(buf);
+ varTree_->viewport()->setUpdatesEnabled(true);
+ varTree_->repaint();
+}
+
+// **************************************************************************
+
+// This is called on program stop to process the locals.
+// Once the locals have been processed we prune the tree of items that are
+// inactive.
+void RDBController::parseLocals(char type, char *buf)
+{
+ varTree_->viewport()->setUpdatesEnabled(false);
+
+ // The locals are always attached to the currentFrame
+ VarFrameRoot *frame = varTree_->findFrame(currentFrame_, viewedThread_);
+ if (!frame)
+ {
+ frame = new VarFrameRoot(varTree_, currentFrame_, viewedThread_);
+ frame->setFrameName(
+ frameStack_->findFrame(currentFrame_, viewedThread_)->frameName());
+ }
+
+ Q_ASSERT(frame);
+
+ if (type == (char) CONSTANTS) {
+ frame->addLocals(buf);
+ } else if (type == (char) CVARS) {
+ frame->addLocals(buf);
+ } else if (type == (char) IVARS) {
+ frame->addLocals(buf);
+ } else {
+ frame->addLocals(buf);
+ frame->setLocals();
+ }
+
+ varTree_->viewport()->setUpdatesEnabled(true);
+ varTree_->repaint();
+}
+
+
+
+// **************************************************************************
+
+void RDBController::parse(char *buf)
+{
+ if (currentCmd_ == 0) {
+ return;
+ }
+
+ if (currentCmd_->isARunCmd()) {
+ parseProgramLocation(buf);
+ } else if (currentCmd_->rawDbgCommand() == "break") {
+ emit rawRDBBreakpointList(buf);
+ } else if (qstrncmp(currentCmd_->rawDbgCommand(), "break ", strlen("break ")) == 0) {
+ parseBreakpointSet(buf);
+ } else if (qstrncmp(currentCmd_->rawDbgCommand(), "watch ", strlen("watch ")) == 0) {
+ parseBreakpointSet(buf);
+ } else if (qstrncmp(currentCmd_->rawDbgCommand(), "display ", strlen("display ")) == 0) {
+ parseDisplay(buf, currentCmd_->rawDbgCommand().data() + strlen("display "));
+ } else if (currentCmd_->rawDbgCommand() == "display") {
+ parseUpdateDisplay(buf);
+ } else if (qstrncmp(currentCmd_->rawDbgCommand(), "undisplay ", strlen("undisplay ")) == 0) {
+ ;
+ } else if (qstrncmp(currentCmd_->rawDbgCommand(), "method instance ", strlen("method instance ")) == 0) {
+ } else if (qstrncmp(currentCmd_->rawDbgCommand(), "method ", strlen("method ")) == 0) {
+ } else if (qstrncmp(currentCmd_->rawDbgCommand(), "pp ", strlen("pp ")) == 0) {
+ parseRequestedData(buf);
+ } else if (currentCmd_->rawDbgCommand() == "thread list") {
+ parseThreadList(buf);
+ } else if ( qstrncmp(currentCmd_->rawDbgCommand(), "up ", strlen("up ")) == 0
+ || qstrncmp(currentCmd_->rawDbgCommand(), "down ", strlen("down ")) == 0 )
+ {
+ parseFrameMove(buf);
+ } else if (qstrncmp(currentCmd_->rawDbgCommand(), "thread switch ", strlen("thread switch ")) == 0) {
+ parseSwitchThread(buf);
+ } else if (currentCmd_->rawDbgCommand() == "thread current") {
+ parseThreadList(buf);
+ } else if (currentCmd_->rawDbgCommand() == "where") {
+ parseBacktraceList(buf);
+ } else if (currentCmd_->rawDbgCommand() == "var global") {
+ parseGlobals(buf);
+ } else if (currentCmd_->rawDbgCommand() == "var local") {
+ parseLocals(LOCALS, buf);
+ } else if (qstrncmp(currentCmd_->rawDbgCommand(), "var instance ", strlen("var instance ")) == 0) {
+ parseLocals(IVARS, buf);
+ } else if (qstrncmp(currentCmd_->rawDbgCommand(), "var class ", strlen("var class ")) == 0) {
+ parseLocals(CVARS, buf);
+ } else if (qstrncmp(currentCmd_->rawDbgCommand(), "var const ", strlen("var const ")) == 0) {
+ parseLocals(CONSTANTS, buf);
+ }
+
+ return;
+}
+
+// **************************************************************************
+
+void RDBController::setBreakpoint(const QCString &BPSetCmd, int key)
+{
+ queueCmd(new RDBSetBreakpointCommand(BPSetCmd, key));
+}
+
+// **************************************************************************
+
+void RDBController::clearBreakpoint(const QCString &BPClearCmd)
+{
+ queueCmd(new RDBCommand(BPClearCmd, NOTRUNCMD, NOTINFOCMD));
+ // Note: this is NOT an info command, because rdb doesn't explictly tell
+ // us that the breakpoint has been deleted, so if we don't have it the
+ // BP list doesn't get updated.
+ queueCmd(new RDBCommand("break", NOTRUNCMD, NOTINFOCMD));
+}
+
+// **************************************************************************
+
+void RDBController::modifyBreakpoint( const Breakpoint& BP )
+{
+ Q_ASSERT(BP.isActionModify());
+ if (BP.dbgId() > 0)
+ {
+ if (BP.changedEnable())
+ queueCmd(new RDBCommand(QCString().sprintf("%s %d",
+ BP.isEnabled() ? "enable" : "disable",
+ BP.dbgId()), NOTRUNCMD, NOTINFOCMD));
+
+ // BP.setDbgProcessing(true);
+ // Note: this is NOT an info command, because rdb doesn't explictly tell
+ // us that the breakpoint has been deleted, so if we don't have it the
+ // BP list doesn't get updated.
+ queueCmd(new RDBCommand("break", NOTRUNCMD, NOTINFOCMD));
+ }
+}
+
+// **************************************************************************
+// SLOTS
+// *****
+// For most of these slots data can only be sent to rdb when it
+// isn't busy and it is running.
+
+// **************************************************************************
+
+void RDBController::slotStart(const QString& ruby_interpreter, const QString& character_coding, const QString& run_directory, const QString& debuggee_path, const QString &application, const QString& run_arguments, bool show_constants, bool trace_into_ruby)
+{
+ Q_ASSERT (!dbgProcess_ && !tty_);
+
+// tty_ = new STTY(config_dbgTerminal_, "konsole");
+ tty_ = new STTY(config_dbgTerminal_, Settings::terminalEmulatorName( *kapp->config() ));
+ if (!config_dbgTerminal_)
+ {
+ connect( tty_, SIGNAL(OutOutput(const char*)), SIGNAL(ttyStdout(const char*)) );
+ connect( tty_, SIGNAL(ErrOutput(const char*)), SIGNAL(ttyStderr(const char*)) );
+ }
+
+ QString tty(tty_->getSlave());
+ if (tty.isEmpty())
+ {
+ KMessageBox::error(0, i18n("The ruby debugger cannot use the tty* or pty* devices.\n"
+ "Check the settings on /dev/tty* and /dev/pty*\n"
+ "As root you may need to \"chmod ug+rw\" tty* and pty* devices "
+ "and/or add the user to the tty group using "
+ "\"usermod -G tty username\"."));
+
+ delete tty_;
+ tty_ = 0;
+ return;
+ }
+
+ dbgProcess_ = new KProcess;
+
+ connect( dbgProcess_, SIGNAL(receivedStdout(KProcess *, char *, int)),
+ this, SLOT(slotDbgStdout(KProcess *, char *, int)) );
+
+ connect( dbgProcess_, SIGNAL(receivedStderr(KProcess *, char *, int)),
+ this, SLOT(slotDbgStderr(KProcess *, char *, int)) );
+
+ connect( dbgProcess_, SIGNAL(wroteStdin(KProcess *)),
+ this, SLOT(slotDbgWroteStdin(KProcess *)) );
+
+ connect( dbgProcess_, SIGNAL(processExited(KProcess*)),
+ this, SLOT(slotDbgProcessExited(KProcess*)) );
+
+ rubyInterpreter_ = ruby_interpreter;
+ characterCoding_ = character_coding;
+ runDirectory_ = run_directory;
+ debuggeePath_ = debuggee_path;
+ application_ = application;
+ runArguments_ = run_arguments;
+ showConstants_ = show_constants;
+ traceIntoRuby_ = trace_into_ruby;
+
+ *dbgProcess_ << ruby_interpreter;
+ *dbgProcess_ << character_coding;
+ *dbgProcess_ << "-C" << QString(QFile::encodeName( run_directory ));
+ *dbgProcess_ << "-r" << debuggee_path;
+ *dbgProcess_ << application;
+
+ if (!run_arguments.isNull() && !run_arguments.isEmpty()) {
+ *dbgProcess_ << run_arguments;
+ }
+
+ emit rdbStdout(QString( ruby_interpreter + " " + character_coding
+ + " -C " + QString(QFile::encodeName( run_directory ))
+ + " -r " + debuggee_path + " "
+ + application + " " + run_arguments ).latin1() );
+
+ if (!dbgProcess_->start( KProcess::NotifyOnExit,
+ KProcess::Communication(KProcess::All)) )
+ {
+ kdDebug(9012) << "Couldn't start ruby debugger" << endl;
+ }
+
+ // Initialise rdb. At this stage rdb is sitting wondering what to do,
+ // and to whom. Organise a few things, then set up the tty for the application,
+ // and the application itself
+
+ // Now the ruby debugger has been started and the application has been loaded,
+ // BUT the app hasn't been started yet! A run command is about to be issued
+ // by whoever is controlling us.
+
+ if (!dbgProcess_->writeStdin(QString("%1\n").arg(unixSocketPath_).latin1(), strlen(unixSocketPath_) + 1)) {
+ kdDebug(9012) << "failed to write Unix domain socket path to rdb "
+ << QString("%1\n").arg(unixSocketPath_).latin1() << endl;
+ }
+
+ setStateOff(s_programExited);
+ setStateOn(s_dbgNotStarted|s_appNotStarted|s_silent);
+}
+
+// **************************************************************************
+
+void RDBController::slotStopDebugger()
+{
+ if (stateIsOn(s_shuttingDown) || !dbgProcess_)
+ return;
+
+ setStateOn(s_shuttingDown|s_silent);
+ destroyCmds();
+
+ QTime start;
+ QTime now;
+
+ // Get rdb's attention if it's busy. We need rdb to be at the
+ // command line so we can stop it.
+ if (stateIsOn(s_appBusy))
+ {
+ kdDebug(9012) << "ruby debugger busy on shutdown - stopping rdb (SIGINT)" << endl;
+ dbgProcess_->kill(SIGINT);
+ start = QTime::currentTime();
+ while (-1)
+ {
+ kapp->processEvents(20);
+ now = QTime::currentTime();
+ if (!stateIsOn(s_appBusy) || start.msecsTo( now ) > 2000)
+ break;
+ }
+ }
+
+
+ // Now try to stop the ruby debugger running.
+ kdDebug(9012) << "App is busy" << endl;
+ setStateOn(s_appBusy);
+ const char *quit="quit\n";
+ if (!dbgProcess_->writeStdin(quit, strlen(quit)))
+ kdDebug(9012) << "failed to write 'quit' to ruby debugger" << endl;
+
+ emit rdbStdout("(rdb:1) quit");
+ start = QTime::currentTime();
+ while (-1)
+ {
+ kapp->processEvents(20);
+ now = QTime::currentTime();
+ if (stateIsOn(s_programExited) || start.msecsTo( now ) > 2000)
+ break;
+ }
+
+ // We cannot wait forever.
+ if (!stateIsOn(s_programExited))
+ {
+ kdDebug(9012) << "rdb not shutdown - killing" << endl;
+ dbgProcess_->kill(SIGKILL);
+ }
+
+ delete dbgProcess_; dbgProcess_ = 0;
+ delete tty_; tty_ = 0;
+
+ state_ = s_dbgNotStarted | s_appNotStarted | s_silent;
+ emit dbgStatus (i18n("Debugger stopped"), state_);
+}
+
+
+
+// **************************************************************************
+
+void RDBController::slotRun()
+{
+ if (stateIsOn(s_appBusy|s_dbgNotStarted|s_shuttingDown))
+ return;
+
+ if (stateIsOn(s_programExited)) {
+ slotStart(rubyInterpreter_, characterCoding_, runDirectory_, debuggeePath_, application_, runArguments_, showConstants_, traceIntoRuby_);
+ return;
+ }
+
+ queueCmd(new RDBCommand("cont", RUNCMD, NOTINFOCMD));
+ if (currentCmd_ == 0) {
+ executeCmd();
+ }
+}
+
+// **************************************************************************
+
+void RDBController::slotRunUntil(const QString &fileName, int lineNum)
+{
+ if (stateIsOn(s_appBusy|s_dbgNotStarted|s_shuttingDown))
+ return;
+
+ if (fileName.isEmpty())
+ queueCmd(new RDBCommand( QCString().sprintf("break %d", lineNum),
+ RUNCMD, NOTINFOCMD));
+ else
+ queueCmd(new RDBCommand(
+ QCString().sprintf("break %s:%d", fileName.latin1(), lineNum),
+ RUNCMD, NOTINFOCMD));
+ queueCmd(new RDBCommand("cont", RUNCMD, NOTINFOCMD));
+
+ if (currentCmd_ == 0) {
+ executeCmd();
+ }
+}
+
+// **************************************************************************
+
+void RDBController::slotStepInto()
+{
+ if (stateIsOn(s_appBusy|s_appNotStarted|s_shuttingDown))
+ return;
+
+ queueCmd(new RDBCommand("step", RUNCMD, NOTINFOCMD));
+ if (currentCmd_ == 0) {
+ executeCmd();
+ }
+}
+
+
+// **************************************************************************
+
+void RDBController::slotStepOver()
+{
+ if (stateIsOn(s_appBusy|s_appNotStarted|s_shuttingDown))
+ return;
+
+ queueCmd(new RDBCommand("next", RUNCMD, NOTINFOCMD));
+ if (currentCmd_ == 0) {
+ executeCmd();
+ }
+}
+
+
+// **************************************************************************
+
+void RDBController::slotStepOutOff()
+{
+ if (stateIsOn(s_appBusy|s_appNotStarted|s_shuttingDown))
+ return;
+
+ queueCmd(new RDBCommand("finish", RUNCMD, NOTINFOCMD));
+ if (currentCmd_ == 0) {
+ executeCmd();
+ }
+}
+
+// **************************************************************************
+
+// Only interrupt a running program.
+void RDBController::slotBreakInto()
+{
+ pauseApp();
+}
+
+// **************************************************************************
+
+// See what, if anything needs doing to this breakpoint.
+void RDBController::slotBPState( const Breakpoint& BP )
+{
+ // Are we in a position to do anything to this breakpoint?
+ if (stateIsOn(s_dbgNotStarted|s_shuttingDown) || !BP.isPending() ||
+ BP.isActionDie())
+ return;
+
+ // We need this flag so that we can continue execution. I did use
+ // the s_silent state flag but it can be set prior to this method being
+ // called, hence is invalid.
+ bool restart = false;
+ if (stateIsOn(s_appBusy))
+ {
+ if (!config_forceBPSet_)
+ return;
+
+ // When forcing breakpoints to be set/unset, interrupt a running app
+ // and change the state.
+ setStateOn(s_silent);
+ pauseApp();
+ restart = true;
+ }
+
+ if (BP.isActionAdd())
+ {
+ setBreakpoint(BP.dbgSetCommand().latin1(), BP.key());
+ // BP.setDbgProcessing(true);
+ }
+ else
+ {
+ if (BP.isActionClear())
+ {
+ clearBreakpoint(BP.dbgRemoveCommand().latin1());
+ // BP.setDbgProcessing(true);
+ }
+ else
+ {
+ if (BP.isActionModify())
+ {
+ modifyBreakpoint(BP); // Note: DbgProcessing gets set in modify fn
+ }
+ }
+ }
+
+ if (restart)
+ queueCmd(new RDBCommand("cont", RUNCMD, NOTINFOCMD));
+}
+
+// **************************************************************************
+
+void RDBController::slotClearAllBreakpoints()
+{
+ // Are we in a position to do anything to this breakpoint?
+ if (stateIsOn(s_dbgNotStarted|s_shuttingDown))
+ return;
+
+ bool restart = false;
+ if (stateIsOn(s_appBusy))
+ {
+ if (!config_forceBPSet_)
+ return;
+
+ // When forcing breakpoints to be set/unset, interrupt a running app
+ // and change the state.
+ setStateOn(s_silent);
+ pauseApp();
+ restart = true;
+ }
+
+ queueCmd(new RDBCommand("delete", NOTRUNCMD, NOTINFOCMD));
+ // Note: this is NOT an info command, because rdb doesn't explictly tell
+ // us that the breakpoint has been deleted, so if we don't have it the
+ // BP list doesn't get updated.
+ queueCmd(new RDBCommand("break", NOTRUNCMD, NOTINFOCMD));
+
+ if (restart)
+ queueCmd(new RDBCommand("cont", RUNCMD, NOTINFOCMD));
+
+ executeCmd();
+}
+
+
+
+// **************************************************************************
+
+void RDBController::slotSelectFrame(int frameNo, int threadNo, const QString& frameName)
+{
+ if (stateIsOn(s_appBusy|s_dbgNotStarted|s_shuttingDown)) {
+ kdDebug(9012) << "RDBController::slotSelectFrame wrong state" << endl;
+ return;
+ }
+
+ if (viewedThread_ != threadNo) {
+ // Note that 'thread switch nnn' is a run command
+ queueCmd(new RDBCommand(QCString().sprintf("thread switch %d",
+ threadNo), RUNCMD, INFOCMD));
+ executeCmd();
+ return;
+ }
+
+ if (frameNo > currentFrame_) {
+ queueCmd(new RDBCommand(QCString().sprintf("up %d", frameNo - currentFrame_), NOTRUNCMD, INFOCMD));
+ if (!stateIsOn(s_fetchLocals)) {
+ queueCmd(new RDBCommand("display", NOTRUNCMD, INFOCMD));
+ }
+ } else if (frameNo < currentFrame_) {
+ queueCmd(new RDBCommand(QCString().sprintf("down %d", currentFrame_ - frameNo), NOTRUNCMD, INFOCMD));
+ if (!stateIsOn(s_fetchLocals)) {
+ queueCmd(new RDBCommand("display", NOTRUNCMD, INFOCMD));
+ }
+ }
+
+ // Hold on to this thread/frame so that we know where to put the local
+ // variables if generated.
+ viewedThread_ = threadNo;
+ currentFrame_ = frameNo;
+
+ VarFrameRoot *frame = varTree_->findFrame(frameNo, viewedThread_);
+ if (frame == 0) {
+ frame = new VarFrameRoot(varTree_, currentFrame_, viewedThread_);
+ }
+
+ frame->setFrameName(frameName);
+ varTree_->setSelected(frame, true);
+
+ // Have we already got these details?
+ if (frame->needsVariables()) {
+ // Ask for the locals
+
+ if (showConstants_) {
+ queueCmd(new RDBCommand("var const self.class", NOTRUNCMD, INFOCMD));
+ }
+
+ queueCmd(new RDBCommand("var instance self", NOTRUNCMD, INFOCMD));
+ queueCmd(new RDBCommand("var class self.class", NOTRUNCMD, INFOCMD));
+ queueCmd(new RDBCommand("var local", NOTRUNCMD, INFOCMD));
+ frame->startWaitingForData();
+ }
+
+ if (currentCmd_ == 0) {
+ executeCmd();
+ }
+
+ return;
+}
+
+
+
+// **************************************************************************
+
+// This is called when an item needs special processing to show a value.
+void RDBController::slotExpandItem(VarItem *item, const QCString &userRequest)
+{
+ if (stateIsOn(s_appBusy|s_dbgNotStarted|s_shuttingDown))
+ return;
+
+ Q_ASSERT(item != 0);
+
+ // Bad user data!!
+ if (userRequest.isEmpty())
+ return;
+
+ queueCmd(new RDBItemCommand(item, QCString("pp ") + userRequest.data(), false));
+
+ if (currentCmd_ == 0) {
+ executeCmd();
+ }
+}
+
+// **************************************************************************
+
+// This method evaluates text selected with the 'Inspect:' context menu
+void RDBController::slotRubyInspect(const QString &inspectText)
+{
+ queueCmd(new RDBCommand( QCString().sprintf("p %s", inspectText.latin1()),
+ NOTRUNCMD,
+ INFOCMD ), true );
+ executeCmd();
+}
+
+
+// **************************************************************************
+
+// Add a new expression to be displayed in the Watch variable tree
+void RDBController::slotAddWatchExpression(const QString& expr, bool execute)
+{
+ queueCmd(new RDBCommand( QCString().sprintf("display %s", expr.latin1()),
+ NOTRUNCMD,
+ NOTINFOCMD ) );
+ if (execute) {
+ executeCmd();
+ }
+}
+
+// **************************************************************************
+
+// Add a new expression to be displayed in the Watch variable tree
+void RDBController::slotRemoveWatchExpression(int displayId)
+{
+ queueCmd(new RDBCommand( QCString().sprintf("undisplay %d", displayId),
+ NOTRUNCMD,
+ INFOCMD ) );
+ executeCmd();
+}
+
+
+// **************************************************************************
+
+// The user will only get globals if the Global frame is open
+void RDBController::slotFetchGlobals(bool fetch)
+{
+ if (fetch) {
+ setStateOn(s_fetchGlobals);
+ queueCmd(new RDBCommand("var global", NOTRUNCMD, INFOCMD));
+ executeCmd();
+ } else {
+ setStateOff(s_fetchGlobals);
+ }
+
+ kdDebug(9012) << (fetch ? "<Globals ON>": "<Globals OFF>") << endl;
+}
+
+// **************************************************************************
+
+// Data from the ruby program's stdout gets processed here.
+void RDBController::slotDbgStdout(KProcess *, char *buf, int buflen)
+{
+ QCString msg(buf, buflen+1);
+ emit ttyStdout(msg);
+}
+
+// **************************************************************************
+
+// Data from the ruby program's stderr gets processed here.
+void RDBController::slotDbgStderr(KProcess *, char *buf, int buflen)
+{
+ QCString msg(buf, buflen+1);
+ emit ttyStderr(msg);
+}
+
+// **************************************************************************
+
+void RDBController::slotDbgWroteStdin(KProcess *)
+{
+// setStateOff(s_waitForWrite);
+ // if (!stateIsOn(s_silent))
+ // emit dbgStatus ("", state_);
+// executeCmd();
+}
+
+// **************************************************************************
+
+void RDBController::slotAcceptConnection(int masterSocket)
+{
+ Q_ASSERT(masterSocket == masterSocket_);
+
+ struct sockaddr sockaddr;
+ socklen_t fromlen;
+
+ if (socketNotifier_ != 0) {
+ close(socket_);
+ delete socketNotifier_;
+ }
+
+ socket_ = accept(masterSocket, &sockaddr, &fromlen);
+ if (fcntl(socket_, F_SETFL, O_NONBLOCK) == -1) {
+ kdDebug(9012) << "RDBController::slotAcceptConnection can't set nonblocking socket " << errno << endl;
+ }
+
+ socketNotifier_ = new QSocketNotifier(socket_, QSocketNotifier::Read, 0);
+ QObject::connect( socketNotifier_, SIGNAL(activated(int)),
+ this, SLOT(slotReadFromSocket(int)) );
+
+ setStateOff(s_dbgNotStarted);
+ emit dbgStatus ("", state_);
+
+ cmdList_.clear();
+ rdbOutputLen_ = 0;
+
+ // Organise any breakpoints.
+ emit acceptPendingBPs();
+
+ if (traceIntoRuby_) {
+ queueCmd(new RDBCommand("trace_ruby on", NOTRUNCMD, NOTINFOCMD));
+ }
+
+ queueCmd(new RDBCommand("cont", RUNCMD, NOTINFOCMD));
+
+ // Reset the display id for any watch expressions already in the variable tree
+ varTree_->resetWatchVars();
+}
+
+// **************************************************************************
+
+// Output from rdb via the Unix socket gets processed here.
+void RDBController::slotReadFromSocket(int socket)
+{
+ Q_ASSERT(socket == socket_);
+
+ static bool parsing = false;
+
+ int bytesRead = read(socket, rdbOutput_ + rdbOutputLen_, rdbSizeofBuf_);
+
+ rdbOutputLen_ += bytesRead;
+ *(rdbOutput_ + rdbOutputLen_) = 0;
+
+
+ // Already parsing? then get out quick.
+ if (parsing)
+ {
+ kdDebug(9012) << "Already parsing" << endl;
+ return;
+ }
+
+// kdDebug(9012) << "RDBController::slotReadFromSocket length: " << rdbOutputLen_ << " input: " << rdbOutput_ << endl;
+
+ QRegExp prompt_re("(\\(rdb:(\\d+)\\) )$");
+ int promptPos = prompt_re.search(rdbOutput_, 0);
+
+ // Keep appending output to the rbdOutput_ buffer until the
+ // ruby debugger writes the next prompt
+ if (promptPos == -1) {
+ return;
+ }
+
+// kdDebug(9012) << "RDBController::slotReadFromSocket length: " << rdbOutputLen_ << " input: " << rdbOutput_ << endl;
+
+ // Save the prompt, and remove it from the buffer
+ currentPrompt_ = prompt_re.cap(1).latin1();
+ rdbOutputLen_ -= prompt_re.matchedLength();
+ *(rdbOutput_ + rdbOutputLen_) = 0;
+
+ emit rdbStdout(rdbOutput_);
+
+ parsing = true;
+ parse(rdbOutput_);
+ parsing = false;
+ rdbOutputLen_ = 0;
+
+ executeCmd();
+
+ if (currentCmd_ == 0 && stateIsOn(s_fetchLocals)) {
+ if (!varTree_->schedule()) {
+ setStateOff(s_fetchLocals);
+ }
+ }
+}
+
+// **************************************************************************
+
+void RDBController::slotDbgProcessExited(KProcess*)
+{
+ destroyCmds();
+ state_ = s_appNotStarted|s_programExited|(state_&(s_shuttingDown));
+ emit dbgStatus (i18n("Process exited"), state_);
+ emit rdbStdout("(rdb:1) Process exited\n");
+ frameStack_->clear();
+ varTree_->clear();
+
+ if (socketNotifier_ != 0) {
+ delete socketNotifier_;
+ socketNotifier_ = 0;
+ close(socket_);
+ }
+
+ delete dbgProcess_; dbgProcess_ = 0;
+ delete tty_; tty_ = 0;
+}
+
+
+// **************************************************************************
+
+// Takes abbreviated commands and expands them, before passing them on to rdb
+//
+void RDBController::slotUserRDBCmd(const QString& cmd)
+{
+ kdDebug(9012) << "Requested user cmd: " << cmd << endl;
+ QRegExp break_re("^b(reak)?(\\s.*)?");
+ QRegExp watch_re("^wat(ch)?\\s+(.*)");
+ QRegExp delete_re("^del(ete)?(\\s.*)?");
+ QRegExp display_re("^disp(lay)?(\\s.*)?");
+ QRegExp undisplay_re("^undisp(lay)?(\\s.*)?");
+ QRegExp step_re("^s(tep)?(\\s[\\d]+)?$");
+ QRegExp next_re("^n(ext)?(\\s[\\d]+)?$");
+ QRegExp varlocal_re("^v(ar)?\\s+l(ocal)?");
+ QRegExp varglobal_re("^v(ar)?\\s+g(lobal)?");
+ QRegExp varinstance_re("^v(ar)?\\s+i(nstance)?\\s(.*)");
+ QRegExp varconst_re("^v(ar)?\\s+c(onst)?\\s(.*)");
+ QRegExp threadlist_re("^th(read)?\\s+l(ist)?");
+ QRegExp threadcurrent_re("^th(read)?(\\sc(ur(rent)?)?)?$");
+ QRegExp threadswitch_re("^th(read)?(\\ssw(itch)?)?(\\s.*)");
+ QRegExp thread_re("^th(read)?(\\s+.*)?");
+ QRegExp methodinstance_re("^m(ethod)?\\s+i(nstance)?\\s+(.*)");
+ QRegExp method_re("^m(ethod)?\\s+(.*)");
+ QRegExp list_re("^l(ist)?(\\s+\\d+-\\d+)?$");
+
+ if ( break_re.search(cmd) >= 0 ) {
+ queueCmd(new RDBCommand( QCString().sprintf("break%s", break_re.cap(2).latin1()),
+ NOTRUNCMD,
+ INFOCMD ), true );
+ } else if ( watch_re.search(cmd) >= 0 ) {
+ queueCmd(new RDBCommand( QCString().sprintf("watch %s", watch_re.cap(2).latin1()),
+ NOTRUNCMD,
+ INFOCMD ), true );
+ } else if ( delete_re.search(cmd) >= 0 ) {
+ queueCmd(new RDBCommand( QCString().sprintf("delete%s", delete_re.cap(2).latin1()),
+ NOTRUNCMD,
+ INFOCMD ), true );
+ } else if ( display_re.search(cmd) >= 0 ) {
+ queueCmd(new RDBCommand( QCString().sprintf("display%s", display_re.cap(2).latin1()),
+ NOTRUNCMD,
+ INFOCMD ), true );
+ } else if ( undisplay_re.search(cmd) >= 0 ) {
+ queueCmd(new RDBCommand( QCString().sprintf("undisplay%s", undisplay_re.cap(2).latin1()),
+ NOTRUNCMD,
+ INFOCMD ), true );
+ } else if ( step_re.search(cmd) >= 0 ) {
+ queueCmd(new RDBCommand( QCString().sprintf("step%s", step_re.cap(2).latin1()),
+ RUNCMD,
+ INFOCMD ), true );
+ } else if ( next_re.search(cmd) >= 0 ) {
+ queueCmd(new RDBCommand( QCString().sprintf("next%s", next_re.cap(2).latin1()),
+ RUNCMD,
+ INFOCMD ), true );
+ } else if ( varlocal_re.search(cmd) >= 0 ) {
+ queueCmd(new RDBCommand("var local", NOTRUNCMD, INFOCMD));
+ } else if ( varglobal_re.search(cmd) >= 0 ) {
+ queueCmd(new RDBCommand("var global", NOTRUNCMD, INFOCMD));
+ } else if ( varinstance_re.search(cmd) >= 0 ) {
+ queueCmd(new RDBCommand( QCString().sprintf("var instance %s", varinstance_re.cap(3).latin1()),
+ NOTRUNCMD,
+ INFOCMD ), true );
+ } else if ( varconst_re.search(cmd) >= 0 ) {
+ queueCmd(new RDBCommand( QCString().sprintf("var const %s", varconst_re.cap(3).latin1()),
+ NOTRUNCMD,
+ INFOCMD ), true );
+ } else if ( methodinstance_re.search(cmd) >= 0 ) {
+ queueCmd(new RDBCommand( QCString().sprintf("method instance %s", methodinstance_re.cap(3).latin1()),
+ NOTRUNCMD,
+ INFOCMD ), true );
+ } else if ( method_re.search(cmd) >= 0 ) {
+ queueCmd(new RDBCommand( QCString().sprintf("method %s", method_re.cap(2).latin1()),
+ NOTRUNCMD,
+ INFOCMD ), true );
+ } else if ( list_re.search(cmd) >= 0 ) {
+ queueCmd(new RDBCommand( QCString().sprintf("list%s", list_re.cap(2).latin1()),
+ NOTRUNCMD,
+ INFOCMD ), true );
+ } else if (cmd == "c" || cmd == "cont") {
+ queueCmd(new RDBCommand("cont", RUNCMD, NOTINFOCMD));
+ } else if (cmd == "fi" || cmd == "finish") {
+ queueCmd(new RDBCommand("finish", RUNCMD, NOTINFOCMD));
+ } else if ( threadlist_re.search(cmd) >= 0 ) {
+ queueCmd(new RDBCommand("thread list", NOTRUNCMD, INFOCMD), true);
+ } else if ( threadcurrent_re.search(cmd) >= 0 ) {
+ queueCmd(new RDBCommand("thread current", NOTRUNCMD, INFOCMD), true );
+ } else if ( threadswitch_re.search(cmd) >= 0 ) {
+ queueCmd(new RDBCommand( QCString().sprintf("thread switch%s", threadswitch_re.cap(4).latin1()),
+ RUNCMD,
+ INFOCMD ), true );
+ } else if ( thread_re.search(cmd) >= 0 ) {
+ queueCmd(new RDBCommand( QCString().sprintf("thread%s", thread_re.cap(2).latin1()),
+ NOTRUNCMD,
+ INFOCMD ), true );
+ } else if (cmd == "frame" || cmd == "f" || cmd == "where" || cmd == "w") {
+ queueCmd(new RDBCommand("where", NOTRUNCMD, INFOCMD), true);
+ } else if (cmd == "q" || cmd == "quit") {
+ slotStopDebugger();
+ return;
+ } else {
+ kdDebug(9012) << "Passing directly to rdb: " << cmd << endl;
+ queueCmd(new RDBCommand(cmd.latin1(), NOTRUNCMD, INFOCMD));
+ }
+
+ executeCmd();
+}
+
+}
+
+// **************************************************************************
+// **************************************************************************
+// **************************************************************************
+#include "rdbcontroller.moc"
diff --git a/languages/ruby/debugger/rdbcontroller.h b/languages/ruby/debugger/rdbcontroller.h
new file mode 100644
index 00000000..2d303218
--- /dev/null
+++ b/languages/ruby/debugger/rdbcontroller.h
@@ -0,0 +1,192 @@
+/***************************************************************************
+ rdbcontroller.h - description
+ -------------------
+ begin : Sun Aug 8 1999
+ copyright : (C) 1999 by John Birch
+ email : jbb@kdevelop.org
+
+ Adapted for ruby debugging
+ --------------------------
+
+ begin : Mon Nov 1 2004
+ copyright : (C) 2004 by Richard Dale
+ email : Richard_Dale@tipitina.demon.co.uk
+
+
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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. *
+ * *
+ ***************************************************************************/
+
+#ifndef _RDBCONTROLLER_H_
+#define _RDBCONTROLLER_H_
+
+#include "dbgcontroller.h"
+
+#include <qcstring.h>
+#include <qdom.h>
+#include <qobject.h>
+#include <qptrlist.h>
+#include <qstring.h>
+#include <qsocketnotifier.h>
+
+class KProcess;
+
+namespace RDBDebugger
+{
+
+class Breakpoint;
+class DbgCommand;
+class FramestackWidget;
+class VarItem;
+class VariableTree;
+class STTY;
+
+/**
+ * A front end implementation to the ruby command line debugger
+ * @author jbb
+ */
+
+class RDBController : public DbgController
+{
+ Q_OBJECT
+
+public:
+ RDBController(VariableTree *varTree, FramestackWidget *frameStack, QDomDocument &projectDom);
+ ~RDBController();
+
+protected:
+ void queueCmd(DbgCommand *cmd, bool executeNext=false);
+
+private:
+ void parseProgramLocation (char *buf);
+ void parseBacktraceList (char *buf);
+ void parseThreadList (char* buf);
+ void parseSwitchThread (char* buf);
+ void parseFrameMove (char *buf);
+ void parseBreakpointSet (char *buf);
+ void parseDisplay (char *buf, char * expr);
+ void parseUpdateDisplay (char *buf);
+ void parseGlobals (char *buf);
+ void parseLocals (char type, char *buf);
+ void parseRequestedData (char *buf);
+ void parseFrameSelected (char *buf);
+
+ void parse (char *buf);
+
+ void pauseApp();
+ void executeCmd ();
+ void destroyCmds();
+ void removeInfoRequests();
+ void actOnProgramPause(const QString &msg);
+ void programNoApp(const QString &msg, bool msgBox);
+
+ void setBreakpoint(const QCString &BPSetCmd, int key);
+ void clearBreakpoint(const QCString &BPClearCmd);
+ void modifyBreakpoint(const Breakpoint&);
+
+ void setStateOn(int stateOn) { state_ |= stateOn; }
+ void setStateOff(int stateOff) { state_ &= ~stateOff; }
+ bool stateIsOn(int state) { return state_ &state; }
+
+public slots:
+ void configure();
+
+ void slotStart( const QString& shell, const QString& characterCoding,
+ const QString& run_directory, const QString& debuggee_path,
+ const QString &application, const QString& run_arguments,
+ bool show_constants, bool trace_into_ruby );
+ //void slotStart(const QString& shell, const QString &application);
+
+ void slotStopDebugger();
+
+ void slotRun();
+ void slotRunUntil(const QString &filename, int lineNum);
+ void slotStepInto();
+ void slotStepOver();
+ void slotStepOutOff();
+
+ void slotBreakInto();
+ void slotBPState( const Breakpoint& );
+ void slotClearAllBreakpoints();
+
+ void slotExpandItem(VarItem *parent, const QCString &userRequest);
+ void slotRubyInspect(const QString &inspectText);
+ void slotSelectFrame(int frameNo, int threadNo, const QString& frameName);
+ void slotFetchGlobals(bool fetch);
+ void slotAddWatchExpression(const QString& expr, bool execute);
+ void slotRemoveWatchExpression(int displayId);
+
+ void slotUserRDBCmd(const QString&);
+
+protected slots:
+ void slotDbgStdout(KProcess *proc, char *buf, int buflen);
+ void slotDbgStderr(KProcess *proc, char *buf, int buflen);
+ void slotDbgWroteStdin(KProcess *proc);
+ void slotDbgProcessExited(KProcess *proc);
+
+ void slotAcceptConnection(int masterSocket);
+ void slotReadFromSocket(int socket);
+
+signals:
+ void acceptPendingBPs ();
+ void unableToSetBPNow (int BPNo);
+ void addWatchExpression (const QString&);
+
+private:
+ FramestackWidget* frameStack_;
+ VariableTree* varTree_;
+ int currentFrame_;
+ int viewedThread_;
+
+ int stdoutSizeofBuf_; // size of the buffer for holding stdout piped
+ // from the ruby program
+ int stdoutOutputLen_; // amount of data in the output buffer
+ char* stdoutOutput_; // buffer for the output from kprocess
+ QCString holdingZone_;
+
+ int rdbSizeofBuf_; // size of the output buffer from rdb
+ int rdbOutputLen_; // amount of data in the rdb buffer
+ char* rdbOutput_; // buffer for the output from rdb via the Unix socket
+
+ int masterSocket_; // The socket to accept connections
+ QSocketNotifier* acceptNotifier_;
+ static QCString unixSocketPath_; // The name of the Unix Domain socket
+ int socket_; // The socket to read and write to the debuggee
+ QSocketNotifier* socketNotifier_;
+
+ QPtrList<DbgCommand> cmdList_;
+ DbgCommand* currentCmd_;
+ QString currentPrompt_;
+
+ STTY* tty_;
+
+ // Details for starting the ruby debugger process
+ QString rubyInterpreter_;
+ QString characterCoding_;
+ QString runDirectory_;
+ QString debuggeePath_;
+ QString application_;
+ QString runArguments_;
+ bool showConstants_;
+ bool traceIntoRuby_;
+
+ // Some state variables
+ int state_;
+ bool programHasExited_;
+
+ // Configuration values
+ QDomDocument &dom;
+ bool config_forceBPSet_;
+ bool config_dbgTerminal_;
+};
+
+}
+
+#endif
diff --git a/languages/ruby/debugger/rdboutputwidget.cpp b/languages/ruby/debugger/rdboutputwidget.cpp
new file mode 100644
index 00000000..5e08e116
--- /dev/null
+++ b/languages/ruby/debugger/rdboutputwidget.cpp
@@ -0,0 +1,171 @@
+// *************************************************************************
+// rdboutputwidget.cpp - description
+// -------------------
+// begin : 10th April 2003
+// copyright : (C) 2003 by John Birch
+// email : jbb@kdevelop.org
+//
+// Adapted for ruby debugging
+// --------------------------
+// begin : Mon Nov 1 2004
+// copyright : (C) 2004 by Richard Dale
+// email : Richard_Dale@tipitina.demon.co.uk
+// **************************************************************************
+//
+// **************************************************************************
+// * *
+// * 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 "rdboutputwidget.h"
+#include "dbgcontroller.h"
+
+#include <kcombobox.h>
+#include <kdebug.h>
+#include <kiconloader.h>
+#include <klocale.h>
+
+#include <qlabel.h>
+#include <qlayout.h>
+#include <qtextedit.h>
+#include <qtoolbutton.h>
+#include <qtooltip.h>
+
+/***************************************************************************/
+/***************************************************************************/
+/***************************************************************************/
+
+namespace RDBDebugger
+{
+
+/***************************************************************************/
+
+RDBOutputWidget::RDBOutputWidget( QWidget *parent, const char *name) :
+ QWidget(parent, name),
+ m_userRDBCmdEditor(0),
+ m_Interrupt(0),
+ m_rdbView(0)
+{
+
+ m_rdbView = new QTextEdit (this, name);
+ m_rdbView->setReadOnly(true);
+
+ QBoxLayout *userRDBCmdEntry = new QHBoxLayout();
+ m_userRDBCmdEditor = new KHistoryCombo (this, "rdb-user-cmd-editor");
+
+ QLabel *label = new QLabel(i18n("&RDB cmd:"), this);
+ label->setBuddy(m_userRDBCmdEditor);
+ userRDBCmdEntry->addWidget(label);
+
+ userRDBCmdEntry->addWidget(m_userRDBCmdEditor);
+ userRDBCmdEntry->setStretchFactor(m_userRDBCmdEditor, 1);
+
+ m_Interrupt = new QToolButton( this, "add breakpoint" );
+ m_Interrupt->setSizePolicy ( QSizePolicy ( (QSizePolicy::SizeType)0,
+ ( QSizePolicy::SizeType)0,
+ 0,
+ 0,
+ m_Interrupt->sizePolicy().hasHeightForWidth())
+ );
+ m_Interrupt->setPixmap ( SmallIcon ( "player_pause" ) );
+ userRDBCmdEntry->addWidget(m_Interrupt);
+ QToolTip::add ( m_Interrupt, i18n ( "Pause execution of the app to enter rdb commands" ) );
+
+ QVBoxLayout *topLayout = new QVBoxLayout(this, 2);
+ topLayout->addWidget(m_rdbView, 10);
+ topLayout->addLayout(userRDBCmdEntry);
+
+ slotDbgStatus( "", s_dbgNotStarted);
+
+ connect( m_userRDBCmdEditor, SIGNAL(returnPressed()), SLOT(slotRDBCmd()) );
+ connect( m_Interrupt, SIGNAL(clicked()), SIGNAL(breakInto()));
+}
+
+/***************************************************************************/
+
+RDBOutputWidget::~RDBOutputWidget()
+{
+ delete m_rdbView;
+ delete m_userRDBCmdEditor;
+}
+
+/***************************************************************************/
+
+void RDBOutputWidget::clear()
+{
+ if (m_rdbView)
+ m_rdbView->clear();
+}
+
+/***************************************************************************/
+
+void RDBOutputWidget::slotReceivedStdout(const char* line)
+{
+ if (strncmp(line, "(rdb:", 5) == 0)
+ m_rdbView->append(QString("<font color=\"blue\">").append( line ).append("</font>") );
+ else
+ m_rdbView->append(line);
+}
+
+/***************************************************************************/
+
+void RDBOutputWidget::slotReceivedStderr(const char* line)
+{
+ m_rdbView->append(QString("<font color=\"red\">").append( line ).append("</font>") );
+}
+
+/***************************************************************************/
+
+void RDBOutputWidget::slotRDBCmd()
+{
+ QString RDBCmd(m_userRDBCmdEditor->currentText());
+ if (!RDBCmd.isEmpty())
+ {
+ m_userRDBCmdEditor->addToHistory(RDBCmd);
+ m_userRDBCmdEditor->clearEdit();
+ emit userRDBCmd(RDBCmd);
+ }
+}
+
+/***************************************************************************/
+
+void RDBOutputWidget::slotDbgStatus(const QString &, int statusFlag)
+{
+ if (statusFlag & s_dbgNotStarted)
+ {
+ m_Interrupt->setEnabled(false);
+ m_userRDBCmdEditor->setEnabled(false);
+ return;
+ }
+
+ if (statusFlag & s_appBusy)
+ {
+ m_Interrupt->setEnabled(true);
+ m_userRDBCmdEditor->setEnabled(false);
+ }
+ else
+ {
+ m_Interrupt->setEnabled(false);
+ m_userRDBCmdEditor->setEnabled(true);
+ }
+}
+
+/***************************************************************************/
+
+void RDBOutputWidget::focusInEvent(QFocusEvent */*e*/)
+{
+ m_userRDBCmdEditor->setFocus();
+}
+
+/***************************************************************************/
+/***************************************************************************/
+/***************************************************************************/
+}
+
+
+#include "rdboutputwidget.moc"
+
diff --git a/languages/ruby/debugger/rdboutputwidget.h b/languages/ruby/debugger/rdboutputwidget.h
new file mode 100644
index 00000000..ef466757
--- /dev/null
+++ b/languages/ruby/debugger/rdboutputwidget.h
@@ -0,0 +1,69 @@
+// *************************************************************************
+// gdboutputwidget.cpp - description
+// -------------------
+// begin : 10th April 2003
+// copyright : (C) 2003 by John Birch
+// email : jbb@kdevelop.org
+//
+// Adapted for ruby debugging
+// --------------------------
+// begin : Mon Nov 1 2004
+// copyright : (C) 2004 by Richard Dale
+// email : Richard_Dale@tipitina.demon.co.uk
+// **************************************************************************
+//
+// **************************************************************************
+// * *
+// * 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. *
+// * *
+// **************************************************************************
+
+#ifndef _RDBOUTPUTWIDGET_H_
+#define _RDBOUTPUTWIDGET_H_
+
+#include <qwidget.h>
+
+class KHistoryCombo;
+
+class QTextEdit;
+class QToolButton;
+
+namespace RDBDebugger
+{
+
+class RDBOutputWidget : public QWidget
+{
+ Q_OBJECT
+
+public:
+ RDBOutputWidget( QWidget *parent=0, const char *name=0 );
+ ~RDBOutputWidget();
+
+ void clear();
+
+public slots:
+ void slotReceivedStdout(const char* line);
+ void slotReceivedStderr(const char* line);
+ void slotDbgStatus (const QString &status, int statusFlag);
+
+ void slotRDBCmd();
+
+protected:
+ virtual void focusInEvent(QFocusEvent *e);
+
+signals:
+ void userRDBCmd(const QString &cmd);
+ void breakInto();
+
+private:
+ KHistoryCombo* m_userRDBCmdEditor;
+ QToolButton* m_Interrupt;
+ QTextEdit* m_rdbView;
+};
+
+}
+
+#endif
diff --git a/languages/ruby/debugger/rdbparser.cpp b/languages/ruby/debugger/rdbparser.cpp
new file mode 100644
index 00000000..7792acc5
--- /dev/null
+++ b/languages/ruby/debugger/rdbparser.cpp
@@ -0,0 +1,350 @@
+// **************************************************************************
+// begin : Tue Aug 17 1999
+// copyright : (C) 1999 by John Birch
+// email : jbb@kdevelop.org
+//
+// Adapted for ruby debugging
+// --------------------------
+// begin : Mon Nov 1 2004
+// copyright : (C) 2004 by Richard Dale
+// email : Richard_Dale@tipitina.demon.co.uk
+// **************************************************************************
+
+// **************************************************************************
+// *
+// 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 "rdbparser.h"
+#include "variablewidget.h"
+
+#include <qregexp.h>
+
+#include <ctype.h>
+#include <stdlib.h>
+#include <kdebug.h>
+
+namespace RDBDebugger
+{
+
+
+// **************************************************************************
+
+void RDBParser::parseVariables(LazyFetchItem *parent, char *buf)
+{
+ static const char *unknown = "?";
+
+ QString varName;
+ QCString value;
+ int pos;
+
+ Q_ASSERT(parent);
+ if (buf == 0 || strlen(buf) == 0) {
+ return;
+ }
+
+ if (buf[0] == 0) {
+ buf = (char*)unknown;
+ }
+
+ QRegExp var_re("\\s*([^\\n\\s]+) => ([^\\n]+)");
+ QRegExp ref_re("(#<([^:]|::)+:0x[\\da-f]+)\\s*([^=]*)>?");
+ QRegExp struct_re("#<struct Struct::(\\w+)");
+
+ // Look for 'dataitem => value' pairs. For example:
+ // a => 1
+ // m => #<MyClass:0x30093540 @temp={"z"=>"zed", "p"=>"pee"}, @foobar="hello">
+ //
+ pos = var_re.search(buf);
+ if (pos != -1) {
+ while (pos != -1) {
+ varName = var_re.cap(1);
+ if (ref_re.search(var_re.cap(2)) != -1) {
+ if (var_re.cap(2).contains("=") > 0) {
+ value = (ref_re.cap(1) + ">").latin1();
+ } else {
+ // There are no 'name=value' pairs, as in #<Qt::Color:0x0 #ff0000>
+ value = var_re.cap(2).latin1();
+ }
+ } else if (struct_re.search(var_re.cap(2)) != -1) {
+ value = (QString("#<Struct::") + struct_re.cap(1) + ">").latin1();
+ } else {
+ value = var_re.cap(2).latin1();
+ }
+
+ DataType dataType = determineType((char *) var_re.cap(2).latin1());
+
+ // 'self' variables don't need to be expandable, as their details are
+ // already shown in the current frame. So always make them VALUE_TYPE's.
+ if (varName == "self") {
+ dataType = VALUE_TYPE;
+ }
+
+ setItem(parent, varName, dataType, value);
+
+ pos += var_re.matchedLength();
+ pos = var_re.search(buf, pos);
+ }
+
+ return;
+ }
+}
+
+void RDBParser::parseExpandedVariable(VarItem *parent, char *buf)
+{
+ DataType dataType;
+ int pos;
+ QString varName;
+ QCString value;
+ QRegExp ppref_re("(#<([^:]|::)+:0x[\\da-f]+)([^\\n>]*)(>?)");
+
+ switch (parent->dataType()) {
+ case REFERENCE_TYPE:
+ {
+ // Look for a reference type which has been printed via a 'pp' command, to
+ // expand its sub items on multiple lines. For example:
+ // #<MyClass:0x30093540
+ // @foobar="hello",
+ // @sleeper=#<Thread:0x3008fd18 sleep>,
+ // @temp={"z"=>"zed", "p"=>"pee"}>
+ //
+ QRegExp ppvalue_re("\\s*([^\\n\\s=]+)=([^\\n]+)[,>]");
+
+ pos = ppref_re.search(buf);
+ if (pos != -1) {
+ if (ppref_re.cap(4) != "") {
+ // The value is all on one line, so match against name=value
+ // pairs which can't have commas in their values
+ ppvalue_re = QRegExp("\\s*([^\\s=]+)=([^,>]+)([,>])");
+ }
+
+ pos = ppvalue_re.search(buf, pos);
+
+ while (pos != -1) {
+ varName = ppvalue_re.cap(1);
+
+ if (ppref_re.search(ppvalue_re.cap(2)) != -1) {
+ if (ppvalue_re.cap(2).contains("=") > 0) {
+ value = (ppref_re.cap(1) + ">").latin1();
+ } else {
+ // There are no 'name=value' pairs, as in #<Qt::Color:0x0 #ff0000>
+ value = ppvalue_re.cap(2).latin1();
+ }
+ } else {
+ value = ppvalue_re.cap(2).latin1();
+ }
+
+ dataType = determineType((char *) ppvalue_re.cap(2).latin1());
+ setItem(parent, varName, dataType, value);
+
+ pos += ppvalue_re.matchedLength();
+ pos = ppvalue_re.search(buf, pos);
+ }
+
+ }
+ return;
+ }
+
+ case ARRAY_TYPE:
+ {
+ // Look for a array type which has been printed via a 'pp' command, to
+ // expand its sub items. For example:
+ // [0]="hello"
+ // [1]=#"goodbye"
+ //
+ QRegExp pparray_re("\\s*([^=]+)=([^\\n]+)\\n");
+
+ pos = pparray_re.search(buf);
+
+ while (pos != -1) {
+ varName = pparray_re.cap(1);
+
+ if (ppref_re.search(pparray_re.cap(2)) != -1) {
+ value = (ppref_re.cap(1) + ">").latin1();
+ } else {
+ value = pparray_re.cap(2).latin1();
+ }
+
+ DataType dataType = determineType((char *) pparray_re.cap(2).latin1());
+ setItem(parent, varName, dataType, value);
+
+ pos += pparray_re.matchedLength();
+ pos = pparray_re.search(buf, pos);
+ }
+
+ return;
+ }
+
+ case HASH_TYPE:
+ {
+ // Look for a hash type which has been printed via a 'pp' command, to
+ // expand its sub items. For example:
+ // ["greeting"]="hello"
+ // ["farewell"]="goodbye"
+ //
+ QRegExp pphash_re("\\s*(\\[[^\\]]+\\])=([^\\n]+)\\n");
+ pos = pphash_re.search(buf);
+
+ while (pos != -1) {
+ varName = pphash_re.cap(1);
+ value = pphash_re.cap(2).latin1();
+ DataType dataType = determineType(value.data());
+ setItem(parent, varName, dataType, value);
+
+ pos += pphash_re.matchedLength();
+ pos = pphash_re.search(buf, pos);
+ }
+
+ return;
+ }
+
+ case STRUCT_TYPE:
+ {
+ // Look for a reference type which has been printed via a 'pp' command, to
+ // expand its sub items. For example:
+ // #<Struct::Customer
+ // @foobar="hello",
+ // @sleeper=#<Thread:0x3008fd18 sleep>,
+ // @temp={"z"=>"zed", "p"=>"pee"}>
+ //
+ QRegExp ppstruct_re("(#<Struct::\\w+)\\s([^\\n>]*)(>?)");
+ QRegExp ppvalue_re("\\s*([^\\n\\s=]+)=([^\\n]+)[,>]");
+
+ pos = ppstruct_re.search(buf);
+ if (pos != -1) {
+ if (ppstruct_re.cap(3) != "" && ppvalue_re.search(ppstruct_re.cap(0)) != -1) {
+ // The line ends with a '>', but we have this case now..
+ // If there is only one instance variable, pp puts everything
+ // on a single line:
+ // #<Struct::Customer @foobar="hello">
+ // So search for '@foobar="hello"', to use as the
+ // first name=value pair
+ pos = 0;
+ } else {
+ // Mltiple lines with name=value pairs:
+ // #<Struct::Customer
+ // @foobar="hello",
+ pos = ppvalue_re.search(buf, pos);
+ }
+
+ while (pos != -1) {
+ varName = ppvalue_re.cap(1);
+ value = ppvalue_re.cap(2).latin1();
+ dataType = determineType(value.data());
+ setItem(parent, varName, dataType, value);
+
+ pos += ppvalue_re.matchedLength();
+ pos = ppvalue_re.search(buf, pos);
+ }
+
+ }
+ return;
+ }
+
+ case STRING_TYPE:
+ {
+ // Look for a long String which has been printed via a 'pp' command, to
+ // show it as a sequence of 12 bytes slices in hex. For example:
+ // [0..11]=0x89504e470d0a1a0a0000000d
+ // [12..23]=0x494844520000001600000016
+ //
+ QRegExp ppstring_re("\\s*(\\[[^\\]]+\\])=([^\\n]+)\\n");
+ pos = ppstring_re.search(buf);
+
+ while (pos != -1) {
+ varName = ppstring_re.cap(1);
+ value = ppstring_re.cap(2).latin1();
+ DataType dataType = determineType(value.data());
+ setItem(parent, varName, dataType, value);
+
+ pos += ppstring_re.matchedLength();
+ pos = ppstring_re.search(buf, pos);
+ }
+
+ return;
+ }
+
+ default:
+ Q_ASSERT(false);
+ }
+
+ return;
+}
+
+
+// **************************************************************************
+
+void RDBParser::setItem(LazyFetchItem *parent, const QString &varName,
+ DataType dataType, const QCString &value)
+{
+ VarItem *item = parent->findItem(varName);
+ if (item == 0) {
+ item = new VarItem(parent, varName, dataType);
+ } else {
+ // The dataType of an item can change, so update it
+ item->setDataType(dataType);
+ }
+
+ switch (dataType) {
+ case HASH_TYPE:
+ case ARRAY_TYPE:
+ case REFERENCE_TYPE:
+ case STRUCT_TYPE:
+ case STRING_TYPE:
+ item->setText(VALUE_COLUMN, value);
+ item->setExpandable(true);
+ item->update();
+ break;
+
+ case COLOR_TYPE:
+ case VALUE_TYPE:
+ item->setText(VALUE_COLUMN, value);
+ item->setExpandable(false);
+ break;
+
+ default:
+ break;
+ }
+}
+
+// **************************************************************************
+
+DataType RDBParser::determineType(char *buf)
+{
+ QRegExp array_re("(Array \\(\\d+ element\\(s\\)\\))");
+ QRegExp hash_re("(Hash \\(\\d+ element\\(s\\)\\))");
+ QRegExp string_re("(String \\(length \\d+\\))");
+
+ if (qstrncmp(buf, "#<struct", strlen("#<struct")) == 0) {
+ return STRUCT_TYPE;
+ } else if (qstrncmp(buf, "#<Qt::Color:0x", strlen("#<Qt::Color:0x")) == 0) {
+ return COLOR_TYPE;
+ } else if (qstrncmp(buf, "#<", strlen("#<")) == 0 && strstr(buf, "=") != 0) {
+ // An object instance reference is only expandable and a 'REFERENCE_TYPE'
+ // if it contains an '=' (ie it has at least one '@instance_variable=value').
+ // Otherwise, treat it as a 'VALUE_TYPE'.
+ return REFERENCE_TYPE;
+ } else if (array_re.search(buf) != -1) {
+ return ARRAY_TYPE;
+ } else if (hash_re.search(buf) != -1) {
+ return HASH_TYPE;
+ } else if (string_re.search(buf) != -1) {
+ return STRING_TYPE;
+ } else if (qstrncmp(buf, "nil", strlen("nil")) == 0) {
+// return UNKNOWN_TYPE;
+ return VALUE_TYPE;
+ } else {
+ return VALUE_TYPE;
+ }
+}
+
+
+// **************************************************************************
+// **************************************************************************
+// **************************************************************************
+
+}
diff --git a/languages/ruby/debugger/rdbparser.h b/languages/ruby/debugger/rdbparser.h
new file mode 100644
index 00000000..12bc937e
--- /dev/null
+++ b/languages/ruby/debugger/rdbparser.h
@@ -0,0 +1,41 @@
+/***************************************************************************
+ begin : Tue Aug 17 1999
+ copyright : (C) 1999 by John Birch
+ email : jbb@kdevelop.org
+
+ Adapted for ruby debugging
+ --------------------------
+ begin : Mon Nov 1 2004
+ copyright : (C) 2004 by Richard Dale
+ email : Richard_Dale@tipitina.demon.co.uk
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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. *
+ * *
+ ***************************************************************************/
+
+#ifndef _RDBPARSER_H_
+#define _RDBPARSER_H_
+
+#include "variablewidget.h"
+
+namespace RDBDebugger
+{
+
+namespace RDBParser
+{
+ void parseVariables(LazyFetchItem *parent, char *buf);
+ void parseExpandedVariable(VarItem *parent, char *buf);
+ DataType determineType(char *buf);
+ void setItem( LazyFetchItem *parent, const QString &varName,
+ DataType dataType, const QCString &value );
+}
+
+}
+
+#endif
diff --git a/languages/ruby/debugger/rdbtable.cpp b/languages/ruby/debugger/rdbtable.cpp
new file mode 100644
index 00000000..2f470b2d
--- /dev/null
+++ b/languages/ruby/debugger/rdbtable.cpp
@@ -0,0 +1,62 @@
+/***************************************************************************
+* Copyright (C) 2003 by Alexander Dymo *
+* cloudtemple@mksat.net *
+* *
+* *
+* Adapted for ruby debugging *
+* -------------------------- *
+* begin : Mon Nov 1 2004 *
+* copyright : (C) 2004 by Richard Dale *
+* email : Richard_Dale@tipitina.demon.co.uk *
+* *
+* 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 "rdbtable.h"
+
+namespace RDBDebugger {
+
+RDBTable::RDBTable(QWidget *parent, const char *name)
+ : QTable(parent, name)
+{
+}
+
+RDBTable::RDBTable(int nr, int nc, QWidget * parent, const char * name)
+ : QTable(nr, nc, parent, name)
+{
+}
+
+RDBTable::~RDBTable()
+{
+}
+
+void RDBTable::keyPressEvent( QKeyEvent * e )
+{
+ emit keyPressed(e->key());
+
+ if (e->key() == Key_Return)
+ emit returnPressed();
+ else if (e->key() == Key_F2)
+ emit f2Pressed();
+ else if ((e->text() == QString("a")) && (e->state() == AltButton))
+ {
+ emit insertPressed();
+ return;
+ }
+ else if ((e->text() == QString("A")) && (e->state() == AltButton))
+ {
+ emit insertPressed();
+ return;
+ }
+ else if (e->key() == Key_Delete)
+ emit deletePressed();
+
+ QTable::keyPressEvent(e);
+}
+
+}
+
+#include "rdbtable.moc"
+
diff --git a/languages/ruby/debugger/rdbtable.h b/languages/ruby/debugger/rdbtable.h
new file mode 100644
index 00000000..04f47c2b
--- /dev/null
+++ b/languages/ruby/debugger/rdbtable.h
@@ -0,0 +1,45 @@
+/***************************************************************************
+* Copyright (C) 2003 by Alexander Dymo *
+* cloudtemple@mksat.net *
+* *
+* Adapted for ruby debugging *
+* -------------------------- *
+* begin : Mon Nov 1 2004 *
+* copyright : (C) 2004 by Richard Dale *
+* email : Richard_Dale@tipitina.demon.co.uk *
+* *
+* 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. *
+***************************************************************************/
+#ifndef RDBDEBUGGERRDBTABLE_H
+#define RDBDEBUGGERRDBTABLE_H
+
+#include <qtable.h>
+
+namespace RDBDebugger {
+
+class RDBTable : public QTable
+{
+Q_OBJECT
+public:
+ RDBTable(QWidget *parent = 0, const char *name = 0);
+ RDBTable( int numRows, int numCols, QWidget * parent = 0, const char * name = 0 );
+ ~RDBTable();
+
+ virtual void keyPressEvent ( QKeyEvent * e );
+
+signals:
+ void keyPressed(int key);
+
+ void returnPressed();
+ void f2Pressed();
+ void insertPressed();
+ void deletePressed();
+};
+
+}
+
+#endif
+
diff --git a/languages/ruby/debugger/stty.cpp b/languages/ruby/debugger/stty.cpp
new file mode 100644
index 00000000..44cb3795
--- /dev/null
+++ b/languages/ruby/debugger/stty.cpp
@@ -0,0 +1,370 @@
+/***************************************************************************
+ begin : Mon Sep 13 1999
+ copyright : (C) 1999 by John Birch
+ email : jbb@kdevelop.org
+
+ This code was originally written by Judin Maxim, from the
+ KDEStudio project.
+
+ It was then updated with later code from konsole (KDE).
+
+ It has also been enhanced with an idea from the code in kdbg
+ written by Johannes Sixt<Johannes.Sixt@telecom.at>
+
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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. *
+ * *
+ ***************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifdef __osf__
+#define _XOPEN_SOURCE_EXTENDED
+#define O_NDELAY O_NONBLOCK
+#endif
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+
+#ifdef HAVE_SYS_STROPTS_H
+#include <sys/stropts.h>
+#define _NEW_TTY_CTRL
+#endif
+
+#include <assert.h>
+#include <fcntl.h>
+#include <grp.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <termios.h>
+#include <time.h>
+#include <unistd.h>
+
+#if defined (_HPUX_SOURCE)
+#define _TERMIOS_INCLUDED
+#include <bsdtty.h>
+#endif
+
+#include <qintdict.h>
+#include <qsocketnotifier.h>
+#include <qstring.h>
+#include <qfile.h>
+
+#include <klocale.h>
+#include <kstandarddirs.h>
+#include <kapplication.h>
+
+#include "stty.h"
+
+#define PTY_FILENO 3
+#define BASE_CHOWN "konsole_grantpty"
+
+namespace RDBDebugger
+{
+
+static int chownpty(int fd, int grant)
+// param fd: the fd of a master pty.
+// param grant: 1 to grant, 0 to revoke
+// returns 1 on success 0 on fail
+{
+ void(*tmp)(int) = signal(SIGCHLD,SIG_DFL);
+ pid_t pid = fork();
+ if (pid < 0) {
+ signal(SIGCHLD,tmp);
+ return 0;
+ }
+ if (pid == 0) {
+ /* We pass the master pseudo terminal as file descriptor PTY_FILENO. */
+ if (fd != PTY_FILENO && dup2(fd, PTY_FILENO) < 0)
+ ::exit(1);
+
+ QString path = locate("exe", BASE_CHOWN);
+ execle(QFile::encodeName(path), BASE_CHOWN, grant?"--grant":"--revoke", (void *)0, NULL);
+ ::exit(1); // should not be reached
+ }
+ if (pid > 0) {
+ int w;
+ // retry:
+ int rc = waitpid (pid, &w, 0);
+ if (rc != pid)
+ ::exit(1);
+
+ // { // signal from other child, behave like catchChild.
+ // // guess this gives quite some control chaos...
+ // Shell* sh = shells.find(rc);
+ // if (sh) { shells.remove(rc); sh->doneShell(w); }
+ // goto retry;
+ // }
+ signal(SIGCHLD,tmp);
+ return (rc != -1 && WIFEXITED(w) && WEXITSTATUS(w) == 0);
+ }
+ signal(SIGCHLD,tmp);
+ return 0; //dummy.
+}
+
+// **************************************************************************
+
+STTY::STTY(bool ext, const QString &termAppName)
+ : QObject(),
+ out(0),
+ err(0),
+ ttySlave(""),
+ pid_(0)
+{
+ if (ext) {
+ findExternalTTY(termAppName);
+ } else {
+ fout = findTTY();
+ if (fout >= 0) {
+ ttySlave = QString(tty_slave);
+ out = new QSocketNotifier(fout, QSocketNotifier::Read, this);
+ connect( out, SIGNAL(activated(int)), this, SLOT(OutReceived(int)) );
+ }
+ }
+}
+
+// **************************************************************************
+
+STTY::~STTY()
+{
+ if (pid_)
+ ::kill(pid_, SIGTERM);
+
+ if (out) {
+ ::close(fout);
+ delete out;
+ }
+
+ // if ( err )
+ // {
+ // ::close( ferr );
+ // delete err;
+ // }
+}
+
+// **************************************************************************
+
+int STTY::findTTY()
+{
+ int ptyfd = -1;
+ bool needGrantPty = TRUE;
+
+ // Find a master pty that we can open ////////////////////////////////
+
+#ifdef __sgi__
+ ptyfd = open("/dev/ptmx",O_RDWR);
+ if (ptyfd < 0) {
+ perror("Can't open a pseudo teletype");
+ return(-1);
+ }
+ strncpy(tty_slave, ptsname(ptyfd), 50);
+ grantpt(ptyfd);
+ unlockpt(ptyfd);
+ needGrantPty = FALSE;
+#endif
+
+ // first we try UNIX PTY's
+#ifdef TIOCGPTN
+ strcpy(pty_master,"/dev/ptmx");
+ strcpy(tty_slave,"/dev/pts/");
+ ptyfd = open(pty_master,O_RDWR);
+ if (ptyfd >= 0) { // got the master pty
+ int ptyno;
+ if (ioctl(ptyfd, TIOCGPTN, &ptyno) == 0) {
+ struct stat sbuf;
+ sprintf(tty_slave,"/dev/pts/%d",ptyno);
+ if (stat(tty_slave,&sbuf) == 0 && S_ISCHR(sbuf.st_mode))
+ needGrantPty = FALSE;
+ else {
+ close(ptyfd);
+ ptyfd = -1;
+ }
+ } else {
+ close(ptyfd);
+ ptyfd = -1;
+ }
+ }
+#endif
+
+#if defined(_SCO_DS) || defined(__USLC__) /* SCO OSr5 and UnixWare */
+ if (ptyfd < 0) {
+ for (int idx = 0; idx < 256; idx++)
+ { sprintf(pty_master, "/dev/ptyp%d", idx);
+ sprintf(tty_slave, "/dev/ttyp%d", idx);
+ if (access(tty_slave, F_OK) < 0) { idx = 256; break; }
+ if ((ptyfd = open (pty_master, O_RDWR)) >= 0)
+ { if (access (tty_slave, R_OK|W_OK) == 0) break;
+ close(ptyfd); ptyfd = -1;
+ }
+ }
+ }
+#endif
+ if (ptyfd < 0) { /// \FIXME Linux, Trouble on other systems?
+ for (const char* s3 = "pqrstuvwxyzabcde"; *s3 != 0; s3++) {
+ for (const char* s4 = "0123456789abcdef"; *s4 != 0; s4++) {
+ sprintf(pty_master,"/dev/pty%c%c",*s3,*s4);
+ sprintf(tty_slave,"/dev/tty%c%c",*s3,*s4);
+ if ((ptyfd = open(pty_master, O_RDWR)) >= 0) {
+ if (geteuid() == 0 || access(tty_slave, R_OK|W_OK) == 0)
+ break;
+
+ close(ptyfd);
+ ptyfd = -1;
+ }
+ }
+
+ if (ptyfd >= 0)
+ break;
+ }
+ }
+
+ if (ptyfd >= 0) {
+ if (needGrantPty && !chownpty(ptyfd, TRUE)) {
+ fprintf(stderr,"kdevelop: chownpty failed for device %s::%s.\n",pty_master,tty_slave);
+ fprintf(stderr," : This means the session can be eavesdroped.\n");
+ fprintf(stderr," : Make sure konsole_grantpty is installed and setuid root.\n");
+ }
+
+ ::fcntl(ptyfd, F_SETFL, O_NDELAY);
+#ifdef TIOCSPTLCK
+ int flag = 0;
+ ioctl(ptyfd, TIOCSPTLCK, &flag); // unlock pty
+#endif
+ }
+
+ return ptyfd;
+}
+
+// **************************************************************************
+
+void STTY::OutReceived(int f)
+{
+ char buf[1024];
+ int n;
+
+ // read until socket is empty. We shouldn't be receiving a continuous
+ // stream of data, so the loop is unlikely to cause problems.
+ while ((n = ::read(f, buf, sizeof(buf)-1)) > 0) {
+ *(buf+n) = 0; // a standard string
+ if ( f == fout )
+ emit OutOutput(buf);
+ else
+ emit ErrOutput(buf);
+ }
+}
+
+// **************************************************************************
+
+#define FIFO_FILE "/tmp/debug_tty.XXXXXX"
+
+bool STTY::findExternalTTY(const QString &termApp)
+{
+ QString appName(termApp.isEmpty() ? QString("xterm") : termApp);
+
+ char fifo[] = FIFO_FILE;
+ int fifo_fd;
+ if ((fifo_fd = mkstemp(fifo)) == -1)
+ return false;
+
+ ::close(fifo_fd);
+ ::unlink(fifo);
+
+ // create a fifo that will pass in the tty name
+#ifdef HAVE_MKFIFO
+ if (::mkfifo(fifo, S_IRUSR|S_IWUSR) < 0)
+#else
+ if (::mknod(fifo, S_IFIFO | S_IRUSR|S_IWUSR, 0) < 0)
+#endif
+ return false;
+
+ int pid = ::fork();
+ if (pid < 0) { // No process
+ ::unlink(fifo);
+ return false;
+ }
+
+ if (pid == 0) { // child process
+ /*
+ * Spawn a console that in turn runs a shell script that passes us
+ * back the terminal name and then only sits and waits.
+ */
+
+ const char* prog = appName.latin1();
+ QString script = QString("tty>") + QString(fifo) +
+ QString(";" // fifo name
+ "trap \"\" INT QUIT TSTP;" // ignore various signals
+ "exec<&-;exec>&-;" // close stdin and stdout
+ "while :;do sleep 3600;done");
+ const char* scriptStr = script.latin1();
+ const char* end = 0;
+
+ if ( termApp == "konsole" )
+ {
+ ::execlp( prog, prog,
+ "-caption", i18n("kdevelop: Debug application console").local8Bit().data(),
+ "-e", "sh",
+ "-c", scriptStr,
+ end);
+ }
+ else
+ {
+ ::execlp( prog, prog,
+ "-e", "sh",
+ "-c", scriptStr,
+ end);
+ }
+
+ // Should not get here, as above should always work
+ ::exit(1);
+ }
+
+ // parent process
+ if (pid <= 0)
+ ::exit(1);
+
+ // Open the communication between us (the parent) and the
+ // child (the process running on a tty console)
+ fifo_fd = ::open(fifo, O_RDONLY);
+ if (fifo_fd < 0)
+ return false;
+
+ // Get the ttyname from the fifo buffer that the child process
+ // has sent.
+ char ttyname[50];
+ int n = ::read(fifo_fd, ttyname, sizeof(ttyname)-sizeof(char));
+
+ ::close(fifo_fd);
+ ::unlink(fifo);
+
+ // No name??
+ if (n <= 0)
+ return false;
+
+ // remove whitespace
+ ttyname[n] = 0;
+ if (char* newline = strchr(ttyname, '\n'))
+ *newline = 0; // clobber the new line
+
+ ttySlave = ttyname;
+ pid_ = pid;
+
+ return true;
+}
+
+}
+
+// **************************************************************************
+#include "stty.moc"
diff --git a/languages/ruby/debugger/stty.h b/languages/ruby/debugger/stty.h
new file mode 100644
index 00000000..31c8fbfe
--- /dev/null
+++ b/languages/ruby/debugger/stty.h
@@ -0,0 +1,71 @@
+/***************************************************************************
+ begin : Mon Sep 13 1999
+ copyright : (C) 1999 by John Birch
+ email : jbb@kdevelop.org
+
+ This code was originally written by Judin Maxim, from the
+ KDEStudio project.
+
+ It was then updated with later code from konsole (KDE).
+
+ It has also been enhanced with an idea from the code in kdbg
+ written by Johannes Sixt<Johannes.Sixt@telecom.at>
+
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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. *
+ * *
+ ***************************************************************************/
+
+#ifndef _STTY_H_
+#define _STTY_H_
+
+class QSocketNotifier;
+
+#include <qobject.h>
+#include <qstring.h>
+
+namespace RDBDebugger
+{
+
+class STTY : public QObject
+{
+ Q_OBJECT
+
+public:
+ STTY(bool ext=false, const QString &termAppName=QString());
+ ~STTY();
+
+ QString getSlave() { return ttySlave; };
+
+private slots:
+ void OutReceived(int);
+
+signals:
+ void OutOutput(const char *);
+ void ErrOutput(const char*);
+
+private:
+ int findTTY();
+ bool findExternalTTY(const QString &termApp);
+
+private:
+ int fout;
+ int ferr;
+ QSocketNotifier *out;
+ QSocketNotifier *err;
+ QString ttySlave;
+ int pid_;
+
+ char pty_master[50]; // "/dev/ptyxx" | "/dev/ptmx"
+ char tty_slave[50]; // "/dev/ttyxx" | "/dev/pts/########..."
+};
+
+}
+
+#endif
diff --git a/languages/ruby/debugger/variablewidget.cpp b/languages/ruby/debugger/variablewidget.cpp
new file mode 100644
index 00000000..0dbdce9a
--- /dev/null
+++ b/languages/ruby/debugger/variablewidget.cpp
@@ -0,0 +1,1018 @@
+// **************************************************************************
+// begin : Sun Aug 8 1999
+// copyright : (C) 1999 by John Birch
+// email : jbb@kdevelop.org
+//
+// Adapted for ruby debugging
+// --------------------------
+// begin : Mon Nov 1 2004
+// copyright : (C) 2004 by Richard Dale
+// email : Richard_Dale@tipitina.demon.co.uk
+// **************************************************************************
+
+// **************************************************************************
+// * *
+// * 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 "variablewidget.h"
+#include "rdbparser.h"
+#include "rdbcommand.h"
+
+#include <kdebug.h>
+#include <kpopupmenu.h>
+#include <klineedit.h>
+#include <kdeversion.h>
+
+#include <qheader.h>
+#include <qlabel.h>
+#include <qlayout.h>
+#include <qhbox.h>
+#include <qpainter.h>
+#include <qpushbutton.h>
+#include <qregexp.h>
+#include <qcursor.h>
+#include <klocale.h>
+
+#include <qpoint.h>
+#include <qclipboard.h>
+#include <kapplication.h>
+
+// **************************************************************************
+// **************************************************************************
+// **************************************************************************
+
+namespace RDBDebugger
+{
+
+VariableWidget::VariableWidget(QWidget *parent, const char *name)
+ : QWidget(parent, name)
+{
+ varTree_ = new VariableTree(this);
+ QLabel *label = new QLabel(i18n("E&xpression to watch:"), this);
+
+ QHBox *watchEntry = new QHBox( this );
+ watchVarEditor_ = new KHistoryCombo( watchEntry, "var-to-watch editor");
+ label->setBuddy(watchVarEditor_);
+
+ QPushButton *addButton = new QPushButton(i18n("&Add"), watchEntry );
+ addButton->adjustSize();
+ addButton->setFixedWidth(addButton->width());
+
+ QBoxLayout * vbox = new QVBoxLayout();
+ vbox->addWidget( label );
+ vbox->addWidget( watchEntry );
+
+ QVBoxLayout *topLayout = new QVBoxLayout(this, 2);
+ topLayout->addWidget(varTree_, 10);
+ topLayout->addLayout( vbox );
+
+ connect( addButton, SIGNAL(clicked()), SLOT(slotAddWatchExpression()) );
+ connect( watchVarEditor_, SIGNAL(returnPressed()), SLOT(slotAddWatchExpression()) );
+}
+
+
+// **************************************************************************
+
+void VariableWidget::setEnabled(bool bEnabled)
+{
+ QWidget::setEnabled(bEnabled);
+ if (bEnabled && parentWidget() != 0) {
+ varTree_->setColumnWidth(0, parentWidget()->width()/2);
+ }
+}
+// **************************************************************************
+
+void VariableWidget::slotAddWatchExpression()
+{
+ QString watchVar(watchVarEditor_->currentText());
+ if (!watchVar.isEmpty()) {
+ slotAddWatchExpression(watchVar);
+ }
+}
+
+// **************************************************************************
+
+void VariableWidget::slotAddWatchExpression(const QString &ident)
+{
+ if (!ident.isEmpty()) {
+ watchVarEditor_->addToHistory(ident);
+ varTree_->slotAddWatchExpression(ident);
+ watchVarEditor_->clearEdit();
+ }
+}
+
+// **************************************************************************
+
+void VariableWidget::focusInEvent(QFocusEvent */*e*/)
+{
+ varTree_->setFocus();
+}
+
+void VariableWidget::restorePartialProjectSession(const QDomElement* el)
+{
+ varTree_->watchRoot()->restorePartialProjectSession(el);
+}
+
+void VariableWidget::savePartialProjectSession(QDomElement* el)
+{
+ varTree_->watchRoot()->savePartialProjectSession(el);
+}
+
+// **************************************************************************
+// **************************************************************************
+// **************************************************************************
+
+VariableTree::VariableTree(VariableWidget *parent, const char *name)
+ : KListView(parent, name),
+ QToolTip( viewport() ),
+ activationId_(0),
+ currentThread_(-1),
+ selectedFrame_(0),
+ watchRoot_(0),
+ globalRoot_(0)
+{
+ setRootIsDecorated(true);
+ setAllColumnsShowFocus(true);
+ setColumnWidthMode(0, Manual);
+ setSorting(VAR_NAME_COLUMN);
+ QListView::setSelectionMode(QListView::Single);
+
+ addColumn(i18n("Variable"), 100 );
+ addColumn(i18n("Value"), 100 );
+
+ connect( this, SIGNAL(contextMenu(KListView*, QListViewItem*, const QPoint&)),
+ SLOT(slotContextMenu(KListView*, QListViewItem*)) );
+
+ connect( this, SIGNAL(pressed(QListViewItem*)),
+ this, SLOT(slotPressed(QListViewItem*)) );
+
+ watchRoot_ = new WatchRoot(this);
+}
+
+// **************************************************************************
+
+VariableTree::~VariableTree()
+{
+}
+
+// **************************************************************************
+
+void VariableTree::clear()
+{
+ QListViewItem *sibling = firstChild();
+ while (sibling != 0) {
+ QListViewItem * current = sibling;
+ sibling = sibling->nextSibling();
+ if (current->rtti() != RTTI_WATCH_ROOT) {
+ delete current;
+ }
+ }
+
+ globalRoot_ = 0;
+ selectedFrame_ = 0;
+ return;
+}
+
+// **************************************************************************
+
+void VariableTree::slotContextMenu(KListView *, QListViewItem *item)
+{
+ if (item == 0)
+ return;
+
+ setSelected(item, true); // Need to select this item.
+
+ if (item->parent() != 0) {
+ KPopupMenu popup(this);
+ popup.insertTitle(item->text(VAR_NAME_COLUMN));
+ int idRemoveWatch = -2;
+ if (item->rtti() == RTTI_WATCH_VAR_ITEM) {
+ idRemoveWatch = popup.insertItem( i18n("Remove Watch Expression") );
+ }
+
+ int idCopyToClipboard = popup.insertItem( i18n("Copy to Clipboard") );
+ int res = popup.exec(QCursor::pos());
+
+ if (res == idRemoveWatch) {
+ emit removeWatchExpression(((WatchVarItem*)item)->displayId());
+ delete item;
+ } else if (res == idCopyToClipboard) {
+ QClipboard *qb = KApplication::clipboard();
+ QString text = "{ \"" + item->text( VAR_NAME_COLUMN ) + "\", " +
+ "\"" + item->text( VALUE_COLUMN ) + "\" }";
+
+ qb->setText( text, QClipboard::Clipboard );
+ }
+ }
+}
+
+/***************************************************************************/
+
+void VariableTree::setSelected(QListViewItem * item, bool selected)
+{
+ // Save the last selected VarFrameRoot for slotPressed() to restore
+ if (item->rtti() == RTTI_VAR_FRAME_ROOT && selected) {
+ selectedFrame_ = (VarFrameRoot *) item;
+ }
+
+ QListView::setSelected(item, selected);
+}
+
+/***************************************************************************/
+
+// Makes sure that only VarFrameRoot items can be selected
+void VariableTree::slotPressed(QListViewItem * item)
+{
+ if (item == 0) {
+ return;
+ }
+
+ while (item->rtti() == RTTI_VAR_ITEM) {
+ item = item->parent();
+ }
+
+ if ( item->rtti() == RTTI_GLOBAL_ROOT
+ || item->rtti() == RTTI_WATCH_ROOT
+ || item->rtti() == RTTI_WATCH_VAR_ITEM )
+ {
+ if (selectedFrame_ != 0) {
+ setSelected(selectedFrame_, true);
+ }
+ return;
+ }
+
+ if (item->rtti() == RTTI_VAR_FRAME_ROOT) {
+ VarFrameRoot * frame = (VarFrameRoot*) item;
+ emit selectFrame(frame->frameNo(), frame->threadNo());
+ }
+
+ return;
+}
+
+// **************************************************************************
+
+void VariableTree::prune()
+{
+ QListViewItem *child = firstChild();
+
+ while (child != 0) {
+ QListViewItem *nextChild = child->nextSibling();
+
+ // Only prune var frames, not the watch or global root
+ if (child->rtti() == RTTI_VAR_FRAME_ROOT) {
+ if (((VarFrameRoot*) child)->isActive()) {
+ if (child->isOpen()) {
+ ((VarFrameRoot*) child)->prune();
+ }
+ } else {
+ delete child;
+ }
+ }
+
+ child = nextChild;
+ }
+}
+
+// **************************************************************************
+
+// The debugger has moved onto the next program pause, so invalidate
+// everything in the Variable Tree
+void VariableTree::nextActivationId()
+{
+ activationId_++;
+ globalRoot()->setActivationId();
+ watchRoot()->setActivationId();
+ // ..but that's only the Watch and Global roots
+}
+
+// **************************************************************************
+
+// VarFrameRoot frames in the Variable Tree from the previous program pause,
+// are set active here. Notified by the Frame Stack widget when it parses the
+// backtrace from the 'where' command after a pause.
+//
+// After that, any frames which aren't marked as active must have gone
+// out of scope and will end up pruned.
+void VariableTree::slotFrameActive(int frameNo, int threadNo, const QString& frameName)
+{
+ VarFrameRoot * frame = findFrame(frameNo, threadNo);
+ if (frameNo == 1) {
+ // If the current frame 1 doesn't exist, create it
+ if (frame == 0) {
+ frame = new VarFrameRoot(this, frameNo, threadNo);
+ }
+
+ frame->setFrameName(frameName);
+ }
+
+ if (frame != 0 && frame->text(VAR_NAME_COLUMN) == frameName) {
+ frame->setActivationId();
+ }
+}
+
+// **************************************************************************
+
+bool VariableTree::schedule()
+{
+ QListViewItem * child = firstChild();
+ VarFrameRoot * frame = 0;
+
+ while (child != 0) {
+ if (child->rtti() == RTTI_VAR_FRAME_ROOT) {
+ frame = (VarFrameRoot *) child;
+ Q_ASSERT( !frame->isWaitingForData() );
+
+ if (frame->needsVariables()) {
+ if (QApplication::overrideCursor() == 0) {
+ QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
+ }
+
+ // Tell the controller to fetch the variable values
+ emit selectFrame(frame->frameNo(), frame->threadNo());
+ return true;
+ }
+ }
+
+ child = child->nextSibling();
+ }
+
+ frame = findFrame(1, currentThread_);
+ Q_ASSERT( frame != 0 );
+ Q_ASSERT( !frame->needsVariables() );
+
+ // All over, nothing left to fetch.
+ // Return to frame 1, and prune the inactive items
+ // from the variable tree..
+ QApplication::restoreOverrideCursor();
+ emit selectFrame(1, currentThread_);
+ prune();
+
+ return false;
+}
+
+// **************************************************************************
+
+void VariableTree::slotAddWatchExpression(const QString &watchVar)
+{
+ new WatchVarItem(watchRoot(), watchVar, UNKNOWN_TYPE);
+ emit addWatchExpression(watchVar, true);
+}
+
+
+// **************************************************************************
+
+void VariableTree::setFetchGlobals(bool fetch)
+{
+ emit fetchGlobals(fetch);
+}
+
+// **************************************************************************
+
+VarFrameRoot *VariableTree::findFrame(int frameNo, int threadNo) const
+{
+ // frames only exist on the top level so we only need to
+ // check the siblings
+ QListViewItem *sibling = firstChild();
+ while (sibling != 0) {
+ if ( sibling->rtti() == RTTI_VAR_FRAME_ROOT
+ && ((VarFrameRoot*) sibling)->frameNo() == frameNo
+ && ((VarFrameRoot*) sibling)->threadNo() == threadNo )
+ {
+ return (VarFrameRoot*) sibling;
+ }
+
+ sibling = sibling->nextSibling();
+ }
+
+ return 0;
+}
+
+// **************************************************************************
+
+WatchRoot *VariableTree::watchRoot()
+{
+ return watchRoot_;
+}
+
+// **************************************************************************
+
+GlobalRoot *VariableTree::globalRoot()
+{
+ if (globalRoot_ == 0) {
+ globalRoot_ = new GlobalRoot(this);
+ }
+
+ return globalRoot_;
+}
+
+// **************************************************************************
+
+// Watch variables can be added before the start of a debugging session,
+// so tell the controller about any already in the tree at start.
+void VariableTree::resetWatchVars()
+{
+ for (QListViewItem *child = watchRoot()->firstChild(); child != 0; child = child->nextSibling()) {
+ ((WatchVarItem*) child)->setDisplayId(-1);
+ emit addWatchExpression(child->text(VAR_NAME_COLUMN), false);
+ }
+}
+
+// **************************************************************************
+
+void VariableTree::maybeTip(const QPoint &p)
+{
+ VarItem * item = dynamic_cast<VarItem*>( itemAt(p) );
+ if (item != 0) {
+ QRect r = itemRect(item);
+ if (r.isValid()) {
+ tip(r, item->tipText());
+ }
+ }
+}
+
+
+// **************************************************************************
+// **************************************************************************
+// **************************************************************************
+
+LazyFetchItem::LazyFetchItem(VariableTree *parent)
+ : KListViewItem(parent),
+ activationId_(0),
+ waitingForData_(false)
+{
+ setActivationId();
+}
+
+// **************************************************************************
+
+LazyFetchItem::LazyFetchItem(LazyFetchItem *parent)
+ : KListViewItem(parent),
+ activationId_(0),
+ waitingForData_(false)
+{
+ setActivationId();
+}
+
+// **************************************************************************
+
+LazyFetchItem::~LazyFetchItem()
+{
+}
+
+// **************************************************************************
+
+void LazyFetchItem::paintCell(QPainter *p, const QColorGroup &cg,
+ int column, int width, int align)
+{
+ if (p == 0) {
+ return;
+ }
+
+ // make toplevel item (watch and frame items) names bold
+ if (column == VAR_NAME_COLUMN && parent() == 0) {
+ QFont f = p->font();
+ f.setBold(true);
+ p->setFont(f);
+ }
+
+ QListViewItem::paintCell( p, cg, column, width, align );
+}
+
+// **************************************************************************
+
+VarItem *LazyFetchItem::findItem(const QString &name) const
+{
+ QListViewItem *child = firstChild();
+
+ // Check the siblings on this branch
+ while (child != 0) {
+ if (child->text(VAR_NAME_COLUMN) == name) {
+ return (VarItem*) child;
+ }
+
+ child = child->nextSibling();
+ }
+
+ return 0;
+}
+
+// **************************************************************************
+
+void LazyFetchItem::prune()
+{
+ QListViewItem *child = firstChild();
+
+ while (child != 0) {
+ LazyFetchItem *item = (LazyFetchItem*) child;
+ child = child->nextSibling();
+ // Never prune a branch if we are waiting on data to arrive.
+ if (!waitingForData_) {
+ if (item->isActive()) {
+ item->prune();
+ } else {
+ delete item;
+ }
+ }
+ }
+}
+
+
+// **************************************************************************
+// **************************************************************************
+// **************************************************************************
+
+VarItem::VarItem(LazyFetchItem *parent, const QString &varName, DataType dataType)
+ : LazyFetchItem (parent),
+ cache_(QCString()),
+ dataType_(dataType),
+ highlight_(false)
+{
+ setText(VAR_NAME_COLUMN, varName);
+ setSelectable(false);
+
+ // Order the VarItems so that globals are first, then
+ // constants, class variables, instance variables and
+ // finally local variables
+
+ // Matches either an array element or a string slice,
+ // Order on the array index or the first number in the
+ // range specifying the slice.
+ QRegExp arrayelement_re("\\[(\\d+)(\\.\\.\\d+)?\\]");
+ key_ = varName;
+
+ if (arrayelement_re.search(varName) != -1) {
+ key_.sprintf("%.6d", arrayelement_re.cap(1).toInt());
+ } else if (key_.startsWith("$")) {
+ key_.prepend("1001"); // Global variable
+ } else if (QRegExp("^[A-Z]").search(varName) != -1) {
+ key_.prepend("1002"); // Constant
+ } else if (key_.startsWith("@@")) {
+ key_.prepend("1003"); // Class variable
+ } else if (key_.startsWith("@")) {
+ key_.prepend("1004"); // Instance variable
+ } else {
+ key_.prepend("1005"); // Local variable or parameter
+ }
+
+// kdDebug(9012) << " ### VarItem::VarItem *CONSTR* " << varName << endl;
+}
+
+// **************************************************************************
+
+VarItem::~VarItem()
+{
+}
+
+QString VarItem::key(int /*column*/, bool /*ascending*/) const
+{
+ return key_;
+}
+
+// **************************************************************************
+
+// Returns the path of a ruby item. If it is an instance variable, assume
+// that there is an attr_accessor method for it.
+// For example, @foobar within instance obj is accessed as obj.foobar.
+// But don't strip off the @ for an instance variable with no path,
+// and leave a plain '@foobar' as it is.
+QString VarItem::fullName() const
+{
+ QString itemName = text(VAR_NAME_COLUMN);
+ QString vPath("");
+ const VarItem *item = this;
+
+ if (item->parent()->rtti() != RTTI_VAR_ITEM) {
+ return itemName;
+ }
+
+ // This stops at the root item (FrameRoot or GlobalRoot)
+ while (item->rtti() == RTTI_VAR_ITEM) {
+ QString itemName = item->text(VAR_NAME_COLUMN);
+
+ if (vPath.startsWith("[")) {
+ // If it's a Hash or an Array, then just insert the value. As
+ // in adding '[0]' to foo.bar to give foo.bar[0]
+ vPath.prepend(itemName);
+ } else {
+ if (vPath.isEmpty()) {
+ vPath = itemName;
+ } else {
+ vPath.prepend(itemName + ".");
+ }
+ }
+ item = (VarItem*) item->parent();
+ }
+
+ // Change 'self.@foobar' to '@foobar'
+ vPath.replace(QRegExp("^self\\.@"), "@");
+
+ // Use instance_variable_get() to access any '@var's in the middle of a path
+ QRegExp re_instance_var("\\.(@[^\\[.]+)");
+ int pos = re_instance_var.search(vPath);
+ while (pos != -1) {
+ vPath.replace( pos,
+ re_instance_var.matchedLength(),
+ QString(".instance_variable_get(:") + re_instance_var.cap(1) + ")" );
+ pos = re_instance_var.search(vPath, pos);
+ }
+
+ return vPath;
+}
+
+// **************************************************************************
+
+void VarItem::setText(int column, const QString &data)
+{
+ setActivationId();
+
+ if (column == VALUE_COLUMN) {
+ highlight_ = (!text(VALUE_COLUMN).isEmpty() && text(VALUE_COLUMN) != data);
+ }
+
+ QListViewItem::setText(column, data);
+ repaint();
+}
+
+// **************************************************************************
+
+void VarItem::expandValue(char *buf)
+{
+ LazyFetchItem::stopWaitingForData();
+ RDBParser::parseExpandedVariable(this, buf);
+}
+
+// **************************************************************************
+
+void VarItem::setOpen(bool open)
+{
+ QListViewItem::setOpen(open);
+
+ Q_ASSERT( dataType_ == REFERENCE_TYPE
+ || dataType_ == ARRAY_TYPE
+ || dataType_ == HASH_TYPE
+ || dataType_ == STRING_TYPE
+ || dataType_ == STRUCT_TYPE );
+
+ update();
+ return;
+}
+
+// **************************************************************************
+
+void VarItem::update()
+{
+ if (isOpen()) {
+ startWaitingForData();
+// emit ((VariableTree*)listView())->expandItem(this, fullName().latin1());
+ ((VariableTree*)listView())->expandItem(this, fullName().latin1());
+ }
+
+ return;
+}
+
+// **************************************************************************
+
+DataType VarItem::dataType() const
+{
+ return dataType_;
+}
+
+// **************************************************************************
+
+void VarItem::setDataType(DataType dataType)
+{
+ dataType_ = dataType;
+}
+
+// **************************************************************************
+
+// Overridden to highlight the changed items
+void VarItem::paintCell(QPainter *p, const QColorGroup &cg,
+ int column, int width, int align)
+{
+ if (p == 0) {
+ return;
+ }
+
+ if (column == VALUE_COLUMN) {
+ // Show color values as colors, and make the text color the same
+ // as the base color
+ if (dataType_ == COLOR_TYPE) {
+ QRegExp color_re("\\s(#.*)>");
+
+ if (color_re.search(text(column)) != -1) {
+ QColorGroup color_cg( cg.foreground(), cg.background(),
+ cg.light(), cg.dark(), cg.mid(),
+ QColor(color_re.cap(1)), QColor(color_re.cap(1)) );
+ QListViewItem::paintCell(p, color_cg, column, width, align);
+ return;
+ }
+ }
+
+ // Highlight recently changed items in red
+ if (highlight_) {
+ QColorGroup hl_cg( cg.foreground(), cg.background(),
+ cg.light(), cg.dark(), cg.mid(),
+ red, cg.base() );
+ QListViewItem::paintCell(p, hl_cg, column, width, align);
+ return;
+ }
+ }
+
+ QListViewItem::paintCell(p, cg, column, width, align);
+ return;
+}
+
+// **************************************************************************
+
+QString VarItem::tipText() const
+{
+ const unsigned int MAX_TOOLTIP_SIZE = 70;
+ QString tip = text(VALUE_COLUMN);
+
+ if (tip.length() < MAX_TOOLTIP_SIZE) {
+ return tip;
+ } else {
+ return tip.mid(0, MAX_TOOLTIP_SIZE - 1) + " [...]";
+ }
+}
+
+// **************************************************************************
+// **************************************************************************
+// **************************************************************************
+
+VarFrameRoot::VarFrameRoot(VariableTree *parent, int frameNo, int threadNo)
+ : LazyFetchItem(parent),
+ needsVariables_(true),
+ frameNo_(frameNo),
+ threadNo_(threadNo),
+ cache_("")
+{
+ setExpandable(true);
+}
+
+// **************************************************************************
+
+VarFrameRoot::~VarFrameRoot()
+{
+}
+
+// **************************************************************************
+
+void VarFrameRoot::addLocals(char *variables)
+{
+ cache_.append(variables);
+}
+
+// **************************************************************************
+
+void VarFrameRoot::setLocals()
+{
+ RDBParser::parseVariables(this, cache_.data());
+ cache_ = "";
+ needsVariables_ = false;
+ stopWaitingForData();
+ prune();
+
+ return;
+}
+
+// **************************************************************************
+
+// Override setOpen so that we can decide what to do when we do change
+// state.
+void VarFrameRoot::setOpen(bool open)
+{
+ bool localsViewChanged = (isOpen() != open);
+ QListViewItem::setOpen(open);
+
+ if (localsViewChanged) {
+ ((VariableTree*)listView())->selectFrame(frameNo_, threadNo_);
+ }
+
+ return;
+}
+
+void VarFrameRoot::setFrameName(const QString &frameName)
+{
+ setText(VAR_NAME_COLUMN, frameName);
+ setText(VALUE_COLUMN, "");
+
+ return;
+}
+
+void VarFrameRoot::setActivationId()
+{
+ LazyFetchItem::setActivationId();
+ stopWaitingForData();
+ needsVariables_ = true;
+ cache_ = "";
+}
+
+bool VarFrameRoot::needsVariables() const
+{
+ return ( text(VAR_NAME_COLUMN).contains("try_initialize") == 0
+ && isOpen()
+ && !isWaitingForData()
+ && needsVariables_ );
+}
+
+// **************************************************************************
+// **************************************************************************
+// **************************************************************************
+// **************************************************************************
+
+
+GlobalRoot::GlobalRoot(VariableTree *parent)
+ : LazyFetchItem(parent)
+{
+ setText(0, i18n("Global"));
+ setExpandable(true);
+ setOpen(false);
+ setSelectable(false);
+}
+
+// **************************************************************************
+
+GlobalRoot::~GlobalRoot()
+{
+}
+
+// **************************************************************************
+
+void GlobalRoot::setGlobals(char * globals)
+{
+ setActivationId();
+ RDBParser::parseVariables(this, globals);
+
+ return;
+}
+
+// **************************************************************************
+
+void GlobalRoot::setOpen(bool open)
+{
+ bool globalsViewChanged = (isOpen() != open);
+ QListViewItem::setOpen(open);
+
+ if (globalsViewChanged) {
+ ((VariableTree*)listView())->setFetchGlobals(isOpen());
+ }
+
+ return;
+}
+
+// **************************************************************************
+// **************************************************************************
+// **************************************************************************
+// **************************************************************************
+
+WatchVarItem::WatchVarItem( LazyFetchItem *parent, const QString &varName, DataType dataType, int displayId )
+ : VarItem(parent, varName, dataType),
+ displayId_(displayId)
+{
+}
+
+// **************************************************************************
+
+WatchVarItem::~WatchVarItem()
+{
+}
+
+// **************************************************************************
+
+void WatchVarItem::setDisplayId(int id)
+{
+ displayId_ = id;
+}
+
+// **************************************************************************
+
+int WatchVarItem::displayId()
+{
+ return displayId_;
+}
+
+// **************************************************************************
+// **************************************************************************
+// **************************************************************************
+// **************************************************************************
+
+WatchRoot::WatchRoot(VariableTree *parent)
+ : LazyFetchItem(parent)
+{
+ setText(VAR_NAME_COLUMN, i18n("Watch"));
+ setOpen(true);
+ setSelectable(false);
+}
+
+// **************************************************************************
+
+WatchRoot::~WatchRoot()
+{
+}
+
+// **************************************************************************
+
+// Sets the initial value of a new Watch item, along with the
+// display id
+void WatchRoot::setWatchExpression(char * buf, char * expression)
+{
+ QString expr(expression);
+ QRegExp display_re("^(\\d+):\\s([^\n]+)\n");
+
+ for ( QListViewItem *child = firstChild();
+ child != 0;
+ child = child->nextSibling() )
+ {
+ WatchVarItem *varItem = (WatchVarItem*) child;
+ if ( varItem->text(VAR_NAME_COLUMN) == expr
+ && varItem->displayId() == -1
+ && display_re.search(buf) >= 0 )
+ {
+ varItem->setDisplayId(display_re.cap(1).toInt());
+ // Skip over the 'thing = ' part of expr to get the value
+ varItem->setText( VALUE_COLUMN,
+ display_re.cap(2).mid(varItem->text(VAR_NAME_COLUMN).length() + strlen(" = ")) );
+ return;
+ }
+ }
+}
+
+// After a program pause, this updates the new value of a Watch item
+// expr is the thing = value part of "1: a = 1", id is the display number
+void WatchRoot::updateWatchExpression(int id, const QString& expr)
+{
+ for ( QListViewItem *child = firstChild();
+ child != 0;
+ child = child->nextSibling() )
+ {
+ WatchVarItem *varItem = (WatchVarItem*) child;
+ if (varItem->displayId() == id) {
+ Q_ASSERT( expr.startsWith(varItem->text(VAR_NAME_COLUMN)) );
+ // Skip over the 'thing = ' part of expr to get the value
+ varItem->setText( VALUE_COLUMN,
+ expr.mid(varItem->text(VAR_NAME_COLUMN).length() + strlen(" = ")) );
+ return;
+ }
+ }
+}
+
+void WatchRoot::savePartialProjectSession(QDomElement* el)
+{
+ QDomDocument domDoc = el->ownerDocument();
+ if (domDoc.isNull()) {
+ return;
+ }
+
+ QDomElement watchEl = domDoc.createElement("watchExpressions");
+
+ for ( QListViewItem *child = firstChild();
+ child != 0;
+ child = child->nextSibling() )
+ {
+ QDomElement subEl = domDoc.createElement("el");
+ subEl.appendChild(domDoc.createTextNode(child->text(VAR_NAME_COLUMN)));
+ watchEl.appendChild(subEl);
+ }
+
+ if (!watchEl.isNull()) {
+ el->appendChild(watchEl);
+ }
+
+ return;
+}
+
+void WatchRoot::restorePartialProjectSession(const QDomElement* el)
+{
+ QDomDocument domDoc = el->ownerDocument();
+ if (domDoc.isNull()) {
+ return;
+ }
+
+ QDomElement watchEl = el->namedItem("watchExpressions").toElement();
+ QDomElement subEl = watchEl.firstChild().toElement();
+
+ while (!subEl.isNull()) {
+ new WatchVarItem(this, subEl.firstChild().toText().data(), UNKNOWN_TYPE);
+
+ subEl = subEl.nextSibling().toElement();
+ }
+
+ return;
+}
+
+// **************************************************************************
+// **************************************************************************
+// **************************************************************************
+
+}
+
+
+#include "variablewidget.moc"
+
diff --git a/languages/ruby/debugger/variablewidget.h b/languages/ruby/debugger/variablewidget.h
new file mode 100644
index 00000000..c608ec45
--- /dev/null
+++ b/languages/ruby/debugger/variablewidget.h
@@ -0,0 +1,348 @@
+/***************************************************************************
+ begin : Sun Aug 8 1999
+ copyright : (C) 1999 by John Birch
+ email : jbb@kdevelop.org
+
+ Adapted for ruby debugging
+ --------------------------
+ begin : Mon Nov 1 2004
+ copyright : (C) 2004 by Richard Dale
+ email : Richard_Dale@tipitina.demon.co.uk
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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. *
+ * *
+ ***************************************************************************/
+
+#ifndef _VARIABLEWIDGET_H_
+#define _VARIABLEWIDGET_H_
+
+#include "rdbcontroller.h"
+
+#include <klistview.h>
+#include <kcombobox.h>
+#include <qwidget.h>
+#include <qtooltip.h>
+#include <kdebug.h>
+
+class KLineEdit;
+
+namespace RDBDebugger
+{
+
+class LazyFetchItem;
+class VarFrameRoot;
+class GlobalRoot;
+class WatchRoot;
+class VarItem;
+class VariableTree;
+class DbgController;
+class Breakpoint;
+
+enum {
+ VAR_NAME_COLUMN = 0,
+ VALUE_COLUMN = 1
+};
+
+enum DataType {
+ UNKNOWN_TYPE,
+ VALUE_TYPE,
+ REFERENCE_TYPE,
+ ARRAY_TYPE,
+ HASH_TYPE,
+ STRUCT_TYPE,
+ COLOR_TYPE,
+ STRING_TYPE
+};
+
+class VariableWidget : public QWidget
+{
+ Q_OBJECT
+
+public:
+ VariableWidget( QWidget *parent=0, const char *name=0 );
+
+ VariableTree *varTree() const
+ { return varTree_; }
+
+ virtual void setEnabled(bool b);
+
+ void restorePartialProjectSession(const QDomElement* el);
+ void savePartialProjectSession(QDomElement* el);
+
+protected:
+ virtual void focusInEvent(QFocusEvent *e);
+
+public slots:
+ void slotAddWatchExpression();
+ void slotAddWatchExpression(const QString &expr);
+
+private:
+ VariableTree *varTree_;
+ KHistoryCombo *watchVarEditor_;
+};
+
+/***************************************************************************/
+/***************************************************************************/
+/***************************************************************************/
+
+class VariableTree : public KListView, public QToolTip
+{
+ Q_OBJECT
+//we need this to be able to emit expandItem() from within LazyFetchItem
+friend class LazyFetchItem;
+
+public:
+ VariableTree( VariableWidget *parent, const char *name=0 );
+ virtual ~VariableTree();
+
+ // Clear everything but the Watch frame
+ void clear();
+
+ int activationId() const { return activationId_; }
+ void nextActivationId();
+
+ VarFrameRoot *findFrame(int frameNo, int threadNo) const;
+
+ GlobalRoot *globalRoot();
+ WatchRoot *watchRoot();
+
+ void resetWatchVars();
+ void setCurrentThread(int currentThread) { currentThread_ = currentThread; }
+
+ // Remove items that are not active
+ void prune();
+
+ // Look for a frame where 'needsVariables()' is true.
+ // If found, send commands to the debugger to fetch
+ // the variable values.
+ // Return true if a fetch has been scheduled, otherwise
+ // false.
+ bool schedule();
+
+ // Tell the controller whether or not to fetch the
+ // values of the global variables
+ void setFetchGlobals(bool fetch);
+
+ // (from QToolTip) Display a tooltip when the cursor is over an item
+ virtual void maybeTip(const QPoint &);
+
+ virtual void setSelected(QListViewItem * item, bool selected);
+
+signals:
+ void toggleWatchpoint(const QString &varName);
+ void selectFrame(int frame, int thread);
+ void expandItem(VarItem *item, const QCString &request);
+ void fetchGlobals(bool fetch);
+ void addWatchExpression(const QString& expr, bool execute);
+ void removeWatchExpression(int displayId);
+
+public slots:
+ void slotAddWatchExpression(const QString& watchVar);
+ void slotFrameActive(int frameNo, int threadNo, const QString& frameName);
+ void slotPressed(QListViewItem * item);
+
+private slots:
+ void slotContextMenu(KListView *, QListViewItem *item);
+
+private:
+ int activationId_;
+ int currentThread_;
+ VarFrameRoot * selectedFrame_;
+
+ WatchRoot * watchRoot_;
+ GlobalRoot * globalRoot_;
+
+ friend class VarFrameRoot;
+ friend class VarItem;
+ friend class WatchRoot;
+};
+
+/***************************************************************************/
+/***************************************************************************/
+/***************************************************************************/
+
+class LazyFetchItem : public KListViewItem
+{
+public:
+ LazyFetchItem(VariableTree *parent);
+ LazyFetchItem(LazyFetchItem *parent);
+
+ virtual ~LazyFetchItem();
+
+ virtual int rtti() const { return RTTI_LAZY_FETCH_ITEM; }
+
+ virtual void prune();
+ virtual VarItem *findItem(const QString& name) const;
+
+ int currentActivationId() const { return ((VariableTree*) listView())->activationId(); }
+ virtual void setActivationId() { activationId_ = currentActivationId(); }
+ bool isActive() const { return activationId_ == currentActivationId(); }
+
+ void startWaitingForData() { waitingForData_ = true; }
+ void stopWaitingForData() { waitingForData_ = false; }
+ bool isWaitingForData() const { return waitingForData_; }
+
+protected:
+ void paintCell( QPainter *p, const QColorGroup &cg,
+ int column, int width, int align );
+
+private:
+ int activationId_;
+ bool waitingForData_;
+};
+
+/***************************************************************************/
+/***************************************************************************/
+/***************************************************************************/
+
+class VarItem : public LazyFetchItem
+{
+public:
+ VarItem( LazyFetchItem *parent, const QString &varName, DataType dataType );
+
+ virtual ~VarItem();
+
+ virtual int rtti() const { return RTTI_VAR_ITEM; }
+ virtual QString key(int column, bool ascending) const;
+
+ QString fullName() const;
+
+ DataType dataType() const;
+ void setDataType(DataType dataType);
+
+ void setOpen(bool open);
+ void setText (int column, const QString& text);
+
+ // Returns the text to be displayed as tooltip (the value)
+ QString tipText() const;
+
+ // If the item is open, fetch details via a pp command
+ void update();
+
+ // The details from the pp command have arrived, parse them
+ // and update the UI
+ void expandValue(char *data);
+
+private:
+ void paintCell( QPainter *p, const QColorGroup &cg,
+ int column, int width, int align );
+
+private:
+ QString key_;
+ QCString cache_;
+ DataType dataType_;
+ bool highlight_;
+};
+
+/***************************************************************************/
+/***************************************************************************/
+/***************************************************************************/
+
+class WatchVarItem : public VarItem
+{
+public:
+ WatchVarItem( LazyFetchItem *parent, const QString &varName, DataType dataType, int displayId = -1);
+
+ virtual ~WatchVarItem();
+
+ virtual int rtti() const { return RTTI_WATCH_VAR_ITEM; }
+
+ // The id returned by rdb after a display expression is added
+ void setDisplayId(int id);
+ int displayId();
+
+private:
+ int displayId_;
+};
+
+/***************************************************************************/
+/***************************************************************************/
+/***************************************************************************/
+
+class VarFrameRoot : public LazyFetchItem
+{
+public:
+ VarFrameRoot(VariableTree *parent, int frame, int thread);
+ virtual ~VarFrameRoot();
+
+ virtual int rtti() const { return RTTI_VAR_FRAME_ROOT; }
+
+ virtual QString key(int column, bool /*ascending*/) const {
+ return QString("%1%2").arg(RTTI_VAR_FRAME_ROOT).arg(text(column));
+ }
+
+ void addLocals(char *variables);
+ void setLocals();
+ void setOpen(bool open);
+
+ void setFrameName(const QString &frameName);
+
+ virtual void setActivationId();
+ bool needsVariables() const;
+
+ int frameNo() { return frameNo_; }
+ int threadNo() { return threadNo_; }
+
+private:
+ bool needsVariables_;
+ int frameNo_;
+ int threadNo_;
+ QCString cache_;
+};
+
+/***************************************************************************/
+/***************************************************************************/
+/***************************************************************************/
+
+class WatchRoot : public LazyFetchItem
+{
+public:
+ WatchRoot(VariableTree * parent);
+ virtual ~WatchRoot();
+
+ virtual int rtti() const { return RTTI_WATCH_ROOT; }
+
+ virtual QString key(int column, bool /*ascending*/) const {
+ return QString("%1%2").arg(RTTI_WATCH_ROOT).arg(text(column));
+ }
+
+ void setWatchExpression(char * buf, char * expr);
+ void updateWatchExpression(int id, const QString& expr);
+
+ void restorePartialProjectSession(const QDomElement* el);
+ void savePartialProjectSession(QDomElement* el);
+};
+
+/***************************************************************************/
+/***************************************************************************/
+/***************************************************************************/
+
+class GlobalRoot : public LazyFetchItem
+{
+public:
+ GlobalRoot(VariableTree * parent);
+ virtual ~GlobalRoot();
+
+ virtual int rtti() const { return RTTI_GLOBAL_ROOT; }
+
+ virtual QString key(int column, bool /*ascending*/) const {
+ return QString("%1%2").arg(RTTI_GLOBAL_ROOT).arg(text(column));
+ }
+
+ void setOpen(bool open);
+ void setGlobals(char * globals);
+};
+
+/***************************************************************************/
+/***************************************************************************/
+/***************************************************************************/
+
+}
+
+#endif