/*************************************************************************** phpdebuggerinterface.cpp ------------------- begin : 2004-03-12 copyright : (C) 2004 Linus McCabe Based on work by Mathieu Kooiman ***************************************************************************/ /**************************************************************************** * * * 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 #include #include #include #include #include #include #include #include #include #include #include #include "quanta.h" #include "document.h" #include "resource.h" #include "project.h" #include "quantadebuggerinterface.h" #include "debuggerclient.h" #include "debuggerbreakpoint.h" #include "debuggerbreakpointlist.h" #include "debuggermanager.h" #include "messageoutput.h" #include "viewmanager.h" #include "quantaview.h" #include "debuggerui.h" #include "debuggervariable.h" #include "pathmapper.h" #include "variableslistview.h" #include "conditionalbreakpointdialog.h" // dialogs #include "debuggervariablesets.h" DebuggerManager::DebuggerManager(TQObject *myparent) : TQObject(myparent) { initActions(); // Create objects m_breakpointList = new DebuggerBreakpointList(); m_pathmapper = new PathMapper(this, "pathmapper"); m_debuggerui = NULL; m_interface = new QuantaDebuggerInterface(this, "interface"); m_client = NULL; } void DebuggerManager::slotNewProjectLoaded(const TQString &projectname, const KURL &, const KURL &) { if(m_client) { disconnect(m_client, TQT_SIGNAL(updateStatus(DebuggerUI::DebuggerStatus)), m_debuggerui, TQT_SLOT(slotStatus(DebuggerUI::DebuggerStatus))); delete m_client; m_client = NULL; } enableAction("*", false); // Remove all breakpoints m_breakpointList->clear(); if(m_debuggerui) { delete m_debuggerui; m_debuggerui = NULL; } //kdDebug(24002) << "DebuggerManager::slotNewProjectLoaded " << projectname << ", " << Project::ref()->debuggerClient << endl; // Load new client if(!projectname.isEmpty()) { TDETrader::OfferList offers = TDETrader::self()->query("Quanta/Debugger"); TDETrader::OfferList::ConstIterator iterDbg; for(iterDbg = offers.begin(); iterDbg != offers.end(); ++iterDbg) { KService::Ptr service = *iterDbg; if(Project::ref()->debuggerClient() == service->name()) { int errCode = 0; //Workaround for dynamic_cast not working correctly on SUSE 10, gcc 4.0.2 //The correct way should be a simple: // m_client = KParts::ComponentFactory::createInstanceFromService(service, this, 0, TQStringList(), &errCode); TQObject* obj = KParts::ComponentFactory::createInstanceFromService(service, this, 0, TQStringList(), &errCode); if (obj && obj->inherits("DebuggerClient")) m_client = static_cast(obj); //kdDebug(24002) << service->name() << " (" << m_client << ")" << endl; if(!m_client) { emit hideSplash(); KMessageBox::error(NULL, i18n("Unable to load the debugger plugin, error code %1 was returned: %2.").arg(errCode).arg(KLibLoader::self()->lastErrorMessage()), i18n("Debugger Error")); } break; } } } // Tell client to load its settings if (m_client) { TQDomNode nodeThisDbg; TQDomDocument *dom = Project::ref()->sessionDom(); TQDomNode projectNode = dom->firstChild().firstChild(); TQDomNode nodeDbg = projectNode.namedItem("debuggers"); if(nodeDbg.isNull()) { nodeDbg = dom->createElement("debuggers"); projectNode.appendChild(nodeDbg); } // Load this project's mapped paths m_pathmapper->readConfig(); // Load this projects debugger's settings nodeThisDbg = nodeDbg.namedItem(m_client->getName()); if(nodeThisDbg.isNull()) { nodeThisDbg = dom->createElement(m_client->getName()); nodeDbg.appendChild(nodeThisDbg); } m_client->readConfig(nodeThisDbg); // recreate UI m_debuggerui = new DebuggerUI(this, "debuggerui"); connect(m_client, TQT_SIGNAL(updateStatus(DebuggerUI::DebuggerStatus)), m_debuggerui, TQT_SLOT(slotStatus(DebuggerUI::DebuggerStatus))); // Load saved breakpoints if(Project::ref()->debuggerPersistentBreakpoints()) { TQDomNode nodeBreakpoints = nodeDbg.namedItem("breakpoints"); if(!nodeBreakpoints.isNull()) { TQDomNode child = nodeBreakpoints.firstChild(); while(!child.isNull()) { DebuggerBreakpoint* bp = new DebuggerBreakpoint(); bp->setFilePath( child.attributes().namedItem("filepath").nodeValue()); bp->setClass( child.attributes().namedItem("class").nodeValue()); bp->setFunction( child.attributes().namedItem("function").nodeValue()); bp->setCondition( child.attributes().namedItem("condition").nodeValue()); bp->setLine( child.attributes().namedItem("line").nodeValue().toLong()); if(child.attributes().namedItem("type").nodeValue() == "true") bp->setType(DebuggerBreakpoint::ConditionalTrue); else if(child.attributes().namedItem("type").nodeValue() == "change") bp->setType(DebuggerBreakpoint::ConditionalChange); else bp->setType(DebuggerBreakpoint::LineBreakpoint); // Update client and ui m_client->addBreakpoint(bp); m_breakpointList->add(bp); // loop child = child.nextSibling(); } } } // Load saved Watches if(Project::ref()->debuggerPersistentWatches()) { TQDomNode nodeWatches = nodeDbg.namedItem("watches"); if(!nodeWatches.isNull()) { TQDomNode child = nodeWatches.firstChild(); while(!child.isNull()) { TQString watch = child.attributes().namedItem("name").nodeValue(); DebuggerVariable *var = new DebuggerVariable(watch, "", DebuggerVariableTypes::Undefined); m_debuggerui->addVariable(var); m_client->addWatch(watch); child = child.nextSibling(); } } } } initClientActions(); // Disable all debugactions that need a session (ie not breakpoints, etc) slotDebugStartSession(); } void DebuggerManager::initActions() { TDEAction * newaction; TDEActionCollection *ac = quantaApp->actionCollection(); if(!ac) return; //Debugger, breakpoint newaction = new TDEAction(i18n("Toggle &Breakpoint"), SmallIcon("debug_breakpoint"), TQt::CTRL+TQt::SHIFT+TQt::Key_B, this, TQT_SLOT(toggleBreakpoint()), ac, "debug_breakpoints_toggle"); newaction->setToolTip(i18n("Toggles a breakpoint at the current cursor location")); newaction = new TDEAction(i18n("&Clear Breakpoints"), 0, this, TQT_SLOT(clearBreakpoints()), ac, "debug_breakpoints_clear"); newaction->setToolTip(i18n("Clears all breakpoints")); newaction = new TDEAction(i18n("Break When..."), SmallIcon("math_int"), 0, this, TQT_SLOT(slotConditionalBreakpoint()), ac, "debug_conditional_break"); newaction->setToolTip(i18n("Adds a new conditional breakpoint")); newaction = new TDEAction(i18n("Break When..."), SmallIcon("math_int"), 0, this, TQT_SLOT(slotConditionalBreakpoint()), ac, "debug_conditional_breakdialog"); newaction->setToolTip(i18n("Adds a new conditional breakpoint")); // Execution newaction = new TDEAction(i18n("Send HTTP R&equest"), SmallIcon("debug_currentline"), 0, this, TQT_SLOT(slotDebugRequest()), ac, "debug_request"); newaction->setToolTip(i18n("Initiate HTTP Request to the server with debugging activated")); newaction = new TDEAction(i18n("&Trace"), SmallIcon("debug_run"), 0, this, TQT_SLOT(slotDebugTrace()), ac, "debug_trace"); newaction->setToolTip(i18n("Traces through the script. If a script is currently not being debugged, it will start in trace mode when started")); newaction = new TDEAction(i18n("&Run"), SmallIcon("debug_leap"), 0, this, TQT_SLOT(slotDebugRun()), ac, "debug_run"); newaction->setToolTip(i18n("Runs the script. If a script is currently not being debugged, it will start in run mode when started")); newaction = new TDEAction(i18n("&Step"), SmallIcon("debug_stepover"), 0, this, TQT_SLOT(slotDebugStepOver()), ac, "debug_stepover"); newaction->setToolTip(i18n("Executes the next line of execution, but does not step into functions or includes")); newaction = new TDEAction(i18n("Step &Into"), SmallIcon("debug_stepinto"), 0, this, TQT_SLOT(slotDebugStepInto()), ac, "debug_stepinto"); newaction->setToolTip(i18n("Executes the next line of execution and steps into it if it is a function call or inclusion of a file")); newaction = new TDEAction(i18n("S&kip"), SmallIcon("debug_skip"), 0, this, TQT_SLOT(slotDebugSkip()), ac, "debug_skip"); newaction->setToolTip(i18n("Skips the next command of execution and makes the next command the current one")); newaction = new TDEAction(i18n("Step &Out"), SmallIcon("debug_stepout"), 0, this, TQT_SLOT(slotDebugStepOut()), ac, "debug_stepout"); newaction->setToolTip(i18n("Executes the rest of the commands in the current function/file and pauses when it is done (when it reaches a higher level in the backtrace)")); newaction = new TDEAction(i18n("&Pause"), SmallIcon("debug_pause"), 0, this, TQT_SLOT(slotDebugPause()), ac, "debug_pause"); newaction->setToolTip(i18n("Pauses the scripts if it is running or tracing. If a script is currently not being debugged, it will start in paused mode when started")); newaction = new TDEAction(i18n("Kill"), SmallIcon("debug_kill"), 0, this, TQT_SLOT(slotDebugKill()), ac, "debug_kill"); newaction->setToolTip(i18n("Kills the currently running script")); newaction = new TDEAction(i18n("Start Session"), SmallIcon("debug_connect"), 0, this, TQT_SLOT(slotDebugStartSession()), ac, "debug_connect"); newaction->setToolTip(i18n("Starts the debugger internally (Makes debugging possible)")); newaction = new TDEAction(i18n("End Session"), SmallIcon("debug_disconnect"), 0, this, TQT_SLOT(slotDebugEndSession()), ac, "debug_disconnect"); newaction->setToolTip(i18n("Stops the debugger internally (debugging not longer possible)")); // Variables newaction = new TDEAction(i18n("Watch Variable"), SmallIcon("math_brace"), 0, this, TQT_SLOT(slotAddWatch()), ac, "debug_addwatch"); newaction->setToolTip(i18n("Adds a variable to the watch list")); newaction = new TDEAction(i18n("Watch Variable"), SmallIcon("math_brace"), 0, this, TQT_SLOT(slotAddWatch()), ac, "debug_addwatchdialog"); newaction->setToolTip(i18n("Adds a variable to the watch list")); newaction = new TDEAction(i18n("Set Value of Variable"), SmallIcon("edit"), 0, this, TQT_SLOT(slotVariableSet()), ac, "debug_variable_set"); newaction->setToolTip(i18n("Changes the value of a variable")); newaction = new TDEAction(i18n("Set Value of Variable"), SmallIcon("edit"), 0, this, TQT_SLOT(slotVariableSet()), ac, "debug_variable_setdialog"); newaction->setToolTip(i18n("Changes the value of a variable")); newaction = new TDEAction(i18n("Open Profiler Output"), SmallIcon("launch"), 0, this, TQT_SLOT(slotProfilerOpen()), ac, "debug_profiler_open"); newaction->setToolTip(i18n("Opens the profiler output file")); enableAction("*", false); } void DebuggerManager::initClientActions() { enableAction("*", false); if(m_client) { // Get actioncollection and add appropriate actions depending on capabilities of the debugger if(m_client->supports(DebuggerClientCapabilities::LineBreakpoints)) enableAction("debug_breakpoints_toggle", true); if(m_client->supports(DebuggerClientCapabilities::LineBreakpoints)) enableAction("debug_breakpoints_clear", true); } } DebuggerManager::~DebuggerManager() { delete m_breakpointList; m_breakpointList = 0L; if(m_client) { disconnect(m_client, TQT_SIGNAL(updateStatus(DebuggerUI::DebuggerStatus)), m_debuggerui, TQT_SLOT(slotStatus(DebuggerUI::DebuggerStatus))); delete m_client; m_client = 0L; } delete m_debuggerui; m_debuggerui = 0L; delete m_interface; m_interface = 0L; delete m_pathmapper; m_pathmapper = 0L; } void DebuggerManager::enableAction(const TQString& action, bool enable) { if(action == "*") { // Enable/Disable all session related actions + connect/disconnect enableAction("debug_request", enable); enableAction("debug_run", enable); enableAction("debug_trace", enable); enableAction("debug_pause", enable); enableAction("debug_kill", enable); enableAction("debug_stepover", enable); enableAction("debug_stepinto", enable); enableAction("debug_stepout", enable); enableAction("debug_skip", enable); enableAction("debug_connect", enable); enableAction("debug_disconnect", enable); enableAction("debug_breakpoints_toggle", enable); enableAction("debug_breakpoints_clear", enable); enableAction("debug_profiler_open", enable); } else { // The action may or may not exist, depending on capabilities of the debugger plugin TDEActionCollection *ac = quantaApp->actionCollection(); if(ac && ac->action(action.ascii())) ac->action(action.ascii())->setEnabled(enable); } } void DebuggerManager::slotRemoveVariable(DebuggerVariable* var) { if(!m_client) return; m_client->removeWatch(var); } void DebuggerManager::slotRemoveBreakpoint(DebuggerBreakpoint* bp) { if(!m_client) return; m_breakpointList->remove(bp); m_client->removeBreakpoint(bp); } void DebuggerManager::slotAddWatch() { kdDebug(24002) << "DebuggerManager::slotAddWatch() " << endl; if(!m_client) return; TQString watch = KInputDialog::getText(i18n("Add Watch"), i18n("Specify variable to watch:"), quantaApp->popupWord); quantaApp->popupWord = ""; if(!watch.isEmpty()) { DebuggerVariable *var = new DebuggerVariable(watch, "", DebuggerVariableTypes::Undefined); m_debuggerui->addVariable(var); m_client->addWatch(watch); } } void DebuggerManager::slotVariableSet() { kdDebug(24002) << "DebuggerManager::slotVariableSet(" << quantaApp->popupWord << ") " << endl; if(!m_client) return; DebuggerVariableSetS dlg; dlg.lineVariable->setText(quantaApp->popupWord); quantaApp->popupWord = ""; if(dlg.exec() == TQDialog::Accepted) { DebuggerVariable var; var.setName(dlg.lineVariable->text()); var.setValue(dlg.lineValue->text()); m_client->variableSetValue(var); } } void DebuggerManager::slotConditionalBreakpoint() { TQString file; kdDebug(24002) << "DebuggerManager::slotConditionalBreakpoint() " << quantaApp->popupWord << endl; if(!m_client) return; Document *w = ViewManager::ref()->activeDocument(); if (w) file = w->url().prettyURL(0, KURL::StripFileProtocol); ConditionalBreakpointDialog dlg(quantaApp->popupWord, file, "", ""); quantaApp->popupWord = ""; if(dlg.exec() == TQDialog::Accepted) { DebuggerBreakpoint * bp = dlg.breakpoint(); if(bp) { m_client->addBreakpoint(bp); m_breakpointList->add(bp); } } } void DebuggerManager::slotDebugStartSession() { if(!m_client) return; m_client->startSession(); } void DebuggerManager::slotDebugEndSession() { if(!m_client) return; m_client->endSession(); } void DebuggerManager::slotDebugRequest() { if(!m_client) return; m_client->request(); } void DebuggerManager::slotDebugTrace() { if(!m_client) return; m_client->trace(); } void DebuggerManager::slotDebugRun() { if(!m_client) return; m_client->run(); } void DebuggerManager::slotDebugSkip() { if(!m_client) return; m_client->skip(); } void DebuggerManager::slotDebugStepOver() { if(!m_client) return; m_client->stepOver(); } void DebuggerManager::slotDebugStepInto() { if(!m_client) return; m_client->stepInto(); } void DebuggerManager::slotDebugPause() { if(!m_client) return; m_client->pause(); } void DebuggerManager::slotDebugKill() { if(!m_client) return; m_client->kill(); } void DebuggerManager::slotDebugStepOut() { if(!m_client) return; m_client->stepOut(); } void DebuggerManager::slotProfilerOpen( ) { if(!m_client) return; m_client->profilerOpen(); } // A new file was opened, tell the debugger so it can tell us about breakpoints etc void DebuggerManager::fileOpened(const TQString& file) { // Set breakpoint markers if we have a bp in the file m_breakpointList->rewind(); DebuggerBreakpoint* bp; while((bp = m_breakpointList->next())) { if(bp->filePath() == file) { setMark(bp->filePath(), bp->line(), true, KTextEditor::MarkInterface::markType02); } } //lets keep the eye on toggling bp's through the editor margin QuantaView *view = ViewManager::ref()->isOpened(KURL::fromPathOrURL(file)); if (view) { ::Document* qdoc = view->document(); if(qdoc) { connectBreakpointSignals(qdoc); } } // Also, if we have a debug-session, let the debugger know... if(m_client) m_client->fileOpened(file); } // Check with editors if breakpoints changed and send all breakpoint (again) to client void DebuggerManager::refreshBreakpoints() { // Resend bps m_breakpointList->rewind(); DebuggerBreakpoint* bp; while((bp = m_breakpointList->next())) { m_client->addBreakpoint(bp); } } // The debug server told us we have a breakpoint, mark it in the file void DebuggerManager::haveBreakpoint (const TQString& file, int line) { setMark(file, line, true, KTextEditor::MarkInterface::markType02); } // The debug server told us we DONT have a breakpoint, remove it void DebuggerManager::havenoBreakpoint (const TQString& file, int line) { DebuggerBreakpoint* br = new DebuggerBreakpoint(file, line); m_breakpointList->remove(br); setMark(file, line, false, KTextEditor::MarkInterface::markType02); m_breakpointList->remove(br); } // New current line bool DebuggerManager::setActiveLine (const TQString& file, int line ) { //Get local filename TQString filename = file; // Remove old active line mark setMark(m_currentFile, m_currentLine, false, KTextEditor::MarkInterface::markType05); // Update vars with active line m_currentFile = filename; m_currentLine = line; // No new current position if(filename.isEmpty() || quantaApp->previewVisible()) return true; // Find new position in editor if(ViewManager::ref()->isOpened(filename) || QExtFileInfo::exists(filename, true, 0L)) quantaApp->gotoFileAndLine(filename, line, 0); else { showStatus(i18n("Unable to open file %1, check your basedirs and mappings.").arg(filename), true); } // Add new active line mark setMark(filename, line, true, KTextEditor::MarkInterface::markType05); return true; } // Set/clear a mark in a document void DebuggerManager::setMark(const TQString& filename, long line, bool set, int mark) { if((!filename.isEmpty()) && ViewManager::ref()->isOpened(filename)) { ::Document* qdoc = ViewManager::ref()->isOpened(filename)->document(); if(qdoc) { disconnectBreakpointSignals(qdoc); KTextEditor::Document* doc = qdoc->doc(); if(doc) { KTextEditor::MarkInterface *markIf = dynamic_cast(doc); if(markIf) { if(set) markIf->addMark(line, mark); else markIf->removeMark(line, mark); } } connectBreakpointSignals(qdoc); } } } void DebuggerManager::connectBreakpointSignals(Document* qdoc) { connect(qdoc, TQT_SIGNAL(breakpointMarked(Document*, int)), this, TQT_SLOT(slotBreakpointMarked(Document*, int))); connect(qdoc, TQT_SIGNAL(breakpointUnmarked(Document*, int)), this, TQT_SLOT(slotBreakpointUnmarked(Document*, int))); } void DebuggerManager::disconnectBreakpointSignals(Document* qdoc) { disconnect(qdoc, TQT_SIGNAL(breakpointMarked(Document*, int)), this, TQT_SLOT(slotBreakpointMarked(Document*, int))); disconnect(qdoc, TQT_SIGNAL(breakpointUnmarked(Document*, int)), this, TQT_SLOT(slotBreakpointUnmarked(Document*, int))); } // Show a status message and optionally put it on the log bool DebuggerManager::showStatus(const TQString& a_message, bool log) { TQString message = a_message; quantaApp->slotStatusMsg(m_client->getName() + ": " + message); if(log) { if(!message.endsWith("\n")) message.append("\n"); quantaApp->messageOutput()->showMessage(m_client->getName() + ": " + message); } return true; } void DebuggerManager::toggleBreakpoint () { Document *w = ViewManager::ref()->activeDocument(); if (w) { uint line, col; w->viewCursorIf->cursorPositionReal(&line, &col); DebuggerBreakpoint* br = m_breakpointList->retrieve(w->url().prettyURL(0, KURL::StripFileProtocol), line); if(!br) { DebuggerBreakpoint* br = new DebuggerBreakpoint(w->url().prettyURL(0, KURL::StripFileProtocol), line); m_breakpointList->add(br); // setMark(w->url().prettyURL(0, KURL::StripFileProtocol), br->line(), true, KTextEditor::MarkInterface::markType02); // FIXME Is this really needed? if(m_client && m_client->isActive()) { DebuggerBreakpoint tmpbp = *br; m_client->addBreakpoint(br); } else // Trigger pathmapper to make sure we have a valid translation... m_pathmapper->mapLocalPathToServer(w->url().prettyURL(0, KURL::StripFileProtocol)); } else { m_breakpointList->remove(br); // setMark(w->url().prettyURL(0, KURL::StripFileProtocol), br->line(), false, KTextEditor::MarkInterface::markType02); // FIXME Is this really needed? if(m_client && m_client->isActive()) { m_client->removeBreakpoint(br); } } } } void DebuggerManager::clearBreakpoints () { m_breakpointList->clear(); } void DebuggerManager::slotBreakpointMarked(Document* qdoc, int line) { DebuggerBreakpoint* br = new DebuggerBreakpoint(qdoc->url().prettyURL(0, KURL::StripFileProtocol), line); m_breakpointList->add(br); if(m_client && m_client->isActive()) { m_client->addBreakpoint(br); } } void DebuggerManager::slotBreakpointUnmarked(Document* qdoc, int line) { TQString filePath = qdoc->url().prettyURL(0, KURL::StripFileProtocol); DebuggerBreakpoint* br = m_breakpointList->retrieve(filePath, line); if (br) { if(m_client && m_client->isActive()) { m_client->removeBreakpoint(br); } m_breakpointList->remove(br); } } void DebuggerManager::updateBreakpointKey( const DebuggerBreakpoint & bp, const TQString & newkey ) { m_breakpointList->updateBreakpointKey(bp, newkey); // Update UI m_debuggerui->deleteBreakpoint(bp); DebuggerBreakpoint bpnew(bp); bpnew.setKey(newkey); m_debuggerui->showBreakpoint(bpnew); } DebuggerBreakpoint * DebuggerManager::findDebuggerBreakpoint( const TQString & key ) { return m_breakpointList->findDebuggerBreakpoint(key); } void DebuggerManager::saveProperties( ) { if (m_client) { TQDomDocument *dom = Project::ref()->sessionDom(); TQDomNode projectNode = dom->firstChild().firstChild(); TQDomNode nodeDbg = projectNode.namedItem("debuggers"); if(nodeDbg.isNull()) { nodeDbg = dom->createElement("debuggers"); projectNode.appendChild(nodeDbg); } // Save breakpoints if(Project::ref()->debuggerPersistentBreakpoints()) { // (Re)create breakpoints section TQDomNode nodeBreakpoints = nodeDbg.namedItem("breakpoints"); if(!nodeBreakpoints.isNull()) nodeBreakpoints.parentNode().removeChild(nodeBreakpoints); if(m_breakpointList->count() > 0) { nodeBreakpoints = dom->createElement("breakpoints"); nodeDbg.appendChild(nodeBreakpoints); // Loop breakpoints and save 'em m_breakpointList->rewind(); DebuggerBreakpoint* bp; while((bp = m_breakpointList->next())) { TQDomElement child = dom->createElement("breakpoint"); child.setAttribute("filepath", bp->filePath()); child.setAttribute("class", bp->inClass()); child.setAttribute("function", bp->inFunction()); child.setAttribute("condition", bp->condition()); child.setAttribute("line", TQString::number(bp->line())); if(bp->type() == DebuggerBreakpoint::ConditionalTrue) child.setAttribute("type", "true"); else if(bp->type() == DebuggerBreakpoint::ConditionalChange) child.setAttribute("type", "change"); else child.setAttribute("type", "line"); nodeBreakpoints.appendChild(child); } } } // Save Watches if(Project::ref()->debuggerPersistentWatches()) { // (Re)create watches section TQDomNode nodeWatches = nodeDbg.namedItem("watches"); if(!nodeWatches.isNull()) nodeWatches.parentNode().removeChild(nodeWatches); if(m_debuggerui->watches()->first()) { nodeWatches = dom->createElement("watches"); nodeDbg.appendChild(nodeWatches); // Loop watches and save 'em for( DebuggerVariable *v = m_debuggerui->watches()->first(); v; v = m_debuggerui->watches()->next()) { TQDomElement child = dom->createElement("watch"); child.setAttribute("name", v->name()); nodeWatches.appendChild(child); } } } } } void DebuggerManager::slotHandleEvent( const TQString & event, const TQString &, const TQString & ) { if(event == "before_project_close") saveProperties(); } #include "debuggermanager.moc"