// ************************************************************************* // gdboutputwidget.cpp - description // ------------------- // begin : 10th April 2003 // copyright : (C) 2003 by John Birch // email : jbb@kdevelop.org // ************************************************************************** // // ************************************************************************** // * * // * This program is free software; you can redistribute it and/or modify * // * it under the terms of the GNU General Public License as published by * // * the Free Software Foundation; either version 2 of the License, or * // * (at your option) any later version. * // * * // ************************************************************************** #include "gdboutputwidget.h" #include "dbgcontroller.h" #include #include #include #include #include #include #include #include #include #include #include #include #include namespace GDBDebugger { /***************************************************************************/ GDBOutputWidget::GDBOutputWidget( TQWidget *parent, const char *name) : TQWidget(parent, name), m_userGDBCmdEditor(0), m_Interrupt(0), m_gdbView(0), showInternalCommands_(false), maxLines_(5000) { m_gdbView = new OutputText(this); m_gdbView->setTextFormat(TQTextEdit::LogText); TQBoxLayout *userGDBCmdEntry = new TQHBoxLayout(); m_userGDBCmdEditor = new KHistoryCombo (this, "gdb-user-cmd-editor"); TQLabel *label = new TQLabel(i18n("&GDB cmd:"), this); label->setBuddy(m_userGDBCmdEditor); userGDBCmdEntry->addWidget(label); userGDBCmdEntry->addWidget(m_userGDBCmdEditor); userGDBCmdEntry->setStretchFactor(m_userGDBCmdEditor, 1); m_Interrupt = new TQToolButton( this, "add breakpoint" ); m_Interrupt->setSizePolicy ( TQSizePolicy ( (TQSizePolicy::SizeType)0, ( TQSizePolicy::SizeType)0, 0, 0, m_Interrupt->sizePolicy().hasHeightForWidth()) ); m_Interrupt->setPixmap ( SmallIcon ( "player_pause" ) ); userGDBCmdEntry->addWidget(m_Interrupt); TQToolTip::add ( m_Interrupt, i18n ( "Pause execution of the app to enter gdb commands" ) ); TQVBoxLayout *topLayout = new TQVBoxLayout(this, 2); topLayout->addWidget(m_gdbView, 10); topLayout->addLayout(userGDBCmdEntry); slotDbgStatus( "", s_dbgNotStarted); connect( m_userGDBCmdEditor, TQT_SIGNAL(returnPressed()), TQT_SLOT(slotGDBCmd()) ); connect( m_Interrupt, TQT_SIGNAL(clicked()), TQT_SIGNAL(breakInto())); connect( &updateTimer_, TQT_SIGNAL(timeout()), this, TQT_SLOT(flushPending())); } /***************************************************************************/ GDBOutputWidget::~GDBOutputWidget() { delete m_gdbView; delete m_userGDBCmdEditor; } /***************************************************************************/ void GDBOutputWidget::clear() { if (m_gdbView) m_gdbView->clear(); userCommands_.clear(); allCommands_.clear(); } /***************************************************************************/ void GDBOutputWidget::slotInternalCommandStdout(const char* line) { newStdoutLine(line, true); } void GDBOutputWidget::slotUserCommandStdout(const char* line) { newStdoutLine(line, false); } namespace { TQString colorify(TQString text, const TQString& color) { // Make sure the newline is at the end of the newly-added // string. This is so that we can always correctly remove // newline inside 'flushPending'. Q_ASSERT(text.endsWith("\n")); if (text.endsWith("\n")) { text.remove(text.length()-1, 1); } text = "" + text + "\n"; return text; } } void GDBOutputWidget::newStdoutLine(const TQString& line, bool internal) { TQString s = html_escape(line); if (s.startsWith("(gdb)")) { s = colorify(s, "blue"); } allCommands_.append(s); allCommandsRaw_.append(line); trimList(allCommands_, maxLines_); trimList(allCommandsRaw_, maxLines_); if (!internal) { userCommands_.append(s); userCommandsRaw_.append(line); trimList(userCommands_, maxLines_); trimList(userCommandsRaw_, maxLines_); } if (!internal || showInternalCommands_) showLine(s); } void GDBOutputWidget::showLine(const TQString& line) { pendingOutput_ += line; // To improve performance, we update the view after some delay. if (!updateTimer_.isActive()) { updateTimer_.start(100, true /* single shot */); } } void GDBOutputWidget::trimList(TQStringList& l, unsigned max_size) { unsigned int length = l.count(); if (length > max_size) { for(int to_delete = length - max_size; to_delete; --to_delete) { l.erase(l.begin()); } } } void GDBOutputWidget::setShowInternalCommands(bool show) { if (show != showInternalCommands_) { showInternalCommands_ = show; // Set of strings to show changes, text edit still has old // set. Refresh. m_gdbView->clear(); TQStringList& newList = showInternalCommands_ ? allCommands_ : userCommands_; TQStringList::iterator i = newList.begin(), e = newList.end(); for(; i != e; ++i) { // Note that color formatting is already applied to '*i'. showLine(*i); } } } /***************************************************************************/ void GDBOutputWidget::slotReceivedStderr(const char* line) { TQString colored = colorify(html_escape(line), "red"); // Errors are shown inside user commands too. allCommands_.append(colored); trimList(allCommands_, maxLines_); userCommands_.append(colored); trimList(userCommands_, maxLines_); allCommandsRaw_.append(line); trimList(allCommandsRaw_, maxLines_); userCommandsRaw_.append(line); trimList(userCommandsRaw_, maxLines_); showLine(colored); } /***************************************************************************/ void GDBOutputWidget::slotGDBCmd() { TQString GDBCmd(m_userGDBCmdEditor->currentText()); if (!GDBCmd.isEmpty()) { m_userGDBCmdEditor->addToHistory(GDBCmd); m_userGDBCmdEditor->clearEdit(); emit userGDBCmd(GDBCmd); } } void GDBOutputWidget::flushPending() { m_gdbView->setUpdatesEnabled(false); // TQTextEdit adds newline after paragraph automatically. // So, remove trailing newline to avoid double newlines. if (pendingOutput_.endsWith("\n")) pendingOutput_.remove(pendingOutput_.length()-1, 1); Q_ASSERT(!pendingOutput_.endsWith("\n")); m_gdbView->append(pendingOutput_); pendingOutput_ = ""; m_gdbView->scrollToBottom(); m_gdbView->setUpdatesEnabled(true); m_gdbView->update(); m_userGDBCmdEditor->setFocus(); } /***************************************************************************/ void GDBOutputWidget::slotDbgStatus(const TQString &, int statusFlag) { if (statusFlag & s_dbgNotStarted) { m_Interrupt->setEnabled(false); m_userGDBCmdEditor->setEnabled(false); return; } else { m_Interrupt->setEnabled(true); } if (statusFlag & s_dbgBusy) { m_userGDBCmdEditor->setEnabled(false); } else { m_userGDBCmdEditor->setEnabled(true); } } /***************************************************************************/ void GDBOutputWidget::focusInEvent(TQFocusEvent */*e*/) { m_gdbView->scrollToBottom(); m_userGDBCmdEditor->setFocus(); } TQString GDBOutputWidget::html_escape(const TQString& s) { TQString r(s); r.replace("<", "<"); r.replace(">", ">"); return r; } void GDBOutputWidget::savePartialProjectSession(TQDomElement* el) { TQDomDocument doc = el->ownerDocument(); TQDomElement showInternal = doc.createElement("showInternalCommands"); showInternal.setAttribute("value", TQString::number(showInternalCommands_)); el->appendChild(showInternal); } void GDBOutputWidget::restorePartialProjectSession(const TQDomElement* el) { TQDomElement showInternal = el->namedItem("showInternalCommands").toElement(); if (!showInternal.isNull()) { showInternalCommands_ = showInternal.attribute("value", "0").toInt(); } } //void OutputText::contextMenuEvent(TQContextMenuEvent* e) TQPopupMenu* OutputText::createPopupMenu(const TQPoint&) { KPopupMenu* popup = new KPopupMenu; int id = popup->insertItem(i18n("Show Internal Commands"), this, TQT_SLOT(toggleShowInternalCommands())); popup->setItemChecked(id, parent_->showInternalCommands_); popup->setWhatsThis( id, i18n( "Controls if commands issued internally by KDevelop " "will be shown or not.
" "This option will affect only future commands, it will not " "add or remove already issued commands from the view.")); popup->insertItem(i18n("Copy All"), this, TQT_SLOT(copyAll())); return popup; } void OutputText::copyAll() { /* See comments for allCommandRaw_ for explanations of this complex logic, as opposed to calling text(). */ TQStringList& raw = parent_->showInternalCommands_ ? parent_->allCommandsRaw_ : parent_->userCommandsRaw_; TQString text; for (unsigned i = 0; i < raw.size(); ++i) text += raw[i]; // Make sure the text is pastable both with Ctrl-C and with // middle click. TQApplication::clipboard()->setText(text, QClipboard::Clipboard); TQApplication::clipboard()->setText(text, QClipboard::Selection); } void OutputText::toggleShowInternalCommands() { parent_->setShowInternalCommands(!parent_->showInternalCommands_); } /***************************************************************************/ /***************************************************************************/ /***************************************************************************/ } #include "gdboutputwidget.moc"