Changeset View
Changeset View
Standalone View
Standalone View
debuggers/gdb/debuggerplugin.cpp
Show All 18 Lines | |||||
19 | // * You should have received a copy of the GNU General Public | 19 | // * You should have received a copy of the GNU General Public | ||
20 | // * License along with this program; if not, write to the | 20 | // * License along with this program; if not, write to the | ||
21 | // * Free Software Foundation, Inc., | 21 | // * Free Software Foundation, Inc., | ||
22 | // * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | 22 | // * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | ||
23 | // */ | 23 | // */ | ||
24 | 24 | | |||
25 | #include "debuggerplugin.h" | 25 | #include "debuggerplugin.h" | ||
26 | 26 | | |||
27 | #include <QDir> | | |||
28 | #include <QToolTip> | | |||
29 | #include <QByteArray> | | |||
30 | #include <QTimer> | | |||
31 | #include <QMenu> | | |||
32 | #include <QDBusConnectionInterface> | | |||
33 | #include <QDBusInterface> | | |||
34 | #include <QSignalMapper> | | |||
35 | #include <QDBusServiceWatcher> | | |||
36 | | ||||
37 | #include <QAction> | | |||
38 | #include <kactioncollection.h> | | |||
39 | #include <KLocalizedString> | | |||
40 | #include <kmainwindow.h> | | |||
41 | #include <kparts/part.h> | | |||
42 | #include <kparts/mainwindow.h> | | |||
43 | #include <kmessagebox.h> | | |||
44 | #include <kpluginfactory.h> | | |||
45 | #include <kaboutdata.h> | | |||
46 | #include <KToolBar> | | |||
47 | #include <KXmlGuiWindow> | | |||
48 | #include <KXMLGUIFactory> | | |||
49 | | ||||
50 | #include <sublime/view.h> | | |||
51 | | ||||
52 | #include <interfaces/icore.h> | | |||
53 | #include <interfaces/iuicontroller.h> | | |||
54 | #include <interfaces/idocumentcontroller.h> | | |||
55 | #include <interfaces/iprojectcontroller.h> | | |||
56 | #include <interfaces/iruncontroller.h> | | |||
57 | #include <interfaces/iproject.h> | | |||
58 | #include <interfaces/context.h> | | |||
59 | #include <interfaces/context.h> | | |||
60 | #include <interfaces/contextmenuextension.h> | | |||
61 | #include <language/interfaces/editorcontext.h> | | |||
62 | #include <interfaces/idebugcontroller.h> | | |||
63 | #include <interfaces/iplugincontroller.h> | | |||
64 | #include <execute/iexecuteplugin.h> | | |||
65 | #include <interfaces/launchconfigurationtype.h> | | |||
66 | 27 | | |||
67 | #include "disassemblewidget.h" | 28 | #include "disassemblewidget.h" | ||
68 | #ifdef KDEV_ENABLE_GDB_ATTACH_DIALOG | | |||
69 | #include "processselection.h" | | |||
70 | #endif | | |||
71 | #include "memviewdlg.h" | 29 | #include "memviewdlg.h" | ||
72 | #include "gdboutputwidget.h" | 30 | #include "gdboutputwidget.h" | ||
73 | #include "dbgglobal.h" | | |||
74 | #include "debugsession.h" | | |||
75 | #include "selectcoredialog.h" | | |||
76 | 31 | | |||
77 | #include <iostream> | | |||
78 | #include "gdbconfigpage.h" | 32 | #include "gdbconfigpage.h" | ||
79 | #include "debugjob.h" | 33 | #include "debugsession.h" | ||
80 | | ||||
81 | | ||||
82 | namespace KDevMI { namespace GDB { | | |||
83 | | ||||
84 | template<class T> | | |||
85 | class DebuggerToolFactory : public KDevelop::IToolViewFactory | | |||
86 | { | | |||
87 | public: | | |||
88 | DebuggerToolFactory(CppDebuggerPlugin* plugin, const QString &id, Qt::DockWidgetArea defaultArea) | | |||
89 | : m_plugin(plugin), m_id(id), m_defaultArea(defaultArea) | | |||
90 | {} | | |||
91 | | ||||
92 | QWidget* create(QWidget *parent = 0) override | | |||
93 | { | | |||
94 | return new T(m_plugin, parent); | | |||
95 | } | | |||
96 | | ||||
97 | QString id() const override | | |||
98 | { | | |||
99 | return m_id; | | |||
100 | } | | |||
101 | | ||||
102 | Qt::DockWidgetArea defaultPosition() override | | |||
103 | { | | |||
104 | return m_defaultArea; | | |||
105 | } | | |||
106 | | ||||
107 | void viewCreated(Sublime::View* view) override | | |||
108 | { | | |||
109 | if (view->widget()->metaObject()->indexOfSignal(QMetaObject::normalizedSignature("requestRaise()")) != -1) | | |||
110 | QObject::connect(view->widget(), SIGNAL(requestRaise()), view, SLOT(requestRaise())); | | |||
111 | } | | |||
112 | | ||||
113 | /* At present, some debugger widgets (e.g. breakpoint) contain actions so that shortcuts | | |||
114 | work, but they don't need any toolbar. So, suppress toolbar action. */ | | |||
115 | QList<QAction*> toolBarActions( QWidget* viewWidget ) const override | | |||
116 | { | | |||
117 | Q_UNUSED(viewWidget); | | |||
118 | return QList<QAction*>(); | | |||
119 | } | | |||
120 | 34 | | |||
121 | private: | 35 | #include <execute/iexecuteplugin.h> | ||
122 | CppDebuggerPlugin* m_plugin; | 36 | #include <interfaces/icore.h> | ||
123 | QString m_id; | 37 | #include <interfaces/idebugcontroller.h> | ||
124 | Qt::DockWidgetArea m_defaultArea; | 38 | #include <interfaces/iplugincontroller.h> | ||
125 | }; | 39 | #include <interfaces/iuicontroller.h> | ||
40 | #include <interfaces/launchconfigurationtype.h> | ||||
126 | 41 | | |||
127 | } // end of namespace GDB | 42 | #include <KPluginFactory> | ||
128 | } // end of namespace KDevMI | | |||
129 | 43 | | |||
130 | using namespace KDevMI::GDB; | 44 | using namespace KDevMI::GDB; | ||
131 | 45 | | |||
46 | | ||||
132 | K_PLUGIN_FACTORY_WITH_JSON(CppDebuggerFactory, "kdevgdb.json", registerPlugin<CppDebuggerPlugin>(); ) | 47 | K_PLUGIN_FACTORY_WITH_JSON(CppDebuggerFactory, "kdevgdb.json", registerPlugin<CppDebuggerPlugin>(); ) | ||
133 | 48 | | |||
134 | CppDebuggerPlugin::CppDebuggerPlugin( QObject *parent, const QVariantList & ) : | 49 | CppDebuggerPlugin::CppDebuggerPlugin(QObject *parent, const QVariantList &) | ||
135 | KDevelop::IPlugin( "kdevgdb", parent ), | 50 | : MIDebuggerPlugin("kdevgdb", parent) | ||
136 | m_config(KSharedConfig::openConfig(), "GDB Debugger") | | |||
137 | { | 51 | { | ||
138 | KDEV_USE_EXTENSION_INTERFACE( KDevelop::IStatus ) | | |||
139 | | ||||
140 | core()->debugController()->initializeUi(); | | |||
141 | | ||||
142 | setXMLFile("kdevgdbui.rc"); | 52 | setXMLFile("kdevgdbui.rc"); | ||
143 | 53 | | |||
144 | disassemblefactory = new DebuggerToolFactory<DisassembleWidget>( | 54 | disassemblefactory = new DebuggerToolFactory<DisassembleWidget, CppDebuggerPlugin>( | ||
145 | this, "org.kdevelop.debugger.DisassemblerView", Qt::BottomDockWidgetArea); | 55 | this, "org.kdevelop.debugger.DisassemblerView", Qt::BottomDockWidgetArea); | ||
146 | 56 | | |||
147 | gdbfactory = new DebuggerToolFactory<GDBOutputWidget>( | 57 | gdbfactory = new DebuggerToolFactory<GDBOutputWidget, CppDebuggerPlugin>( | ||
148 | this, "org.kdevelop.debugger.ConsoleView",Qt::BottomDockWidgetArea); | 58 | this, "org.kdevelop.debugger.ConsoleView",Qt::BottomDockWidgetArea); | ||
149 | 59 | | |||
150 | core()->uiController()->addToolView( | 60 | core()->uiController()->addToolView( | ||
151 | i18n("Disassemble/Registers"), | 61 | i18n("Disassemble/Registers"), | ||
152 | disassemblefactory); | 62 | disassemblefactory); | ||
153 | 63 | | |||
154 | core()->uiController()->addToolView( | 64 | core()->uiController()->addToolView( | ||
155 | i18n("GDB"), | 65 | i18n("GDB"), | ||
156 | gdbfactory); | 66 | gdbfactory); | ||
157 | 67 | | |||
158 | #ifndef WITH_OKTETA | 68 | #ifndef WITH_OKTETA | ||
159 | memoryviewerfactory = nullptr; | 69 | memoryviewerfactory = nullptr; | ||
160 | #else | 70 | #else | ||
161 | memoryviewerfactory = new DebuggerToolFactory<MemoryViewerWidget>( | 71 | memoryviewerfactory = new DebuggerToolFactory<MemoryViewerWidget, CppDebuggerPlugin>( | ||
162 | this, "org.kdevelop.debugger.MemoryView", Qt::BottomDockWidgetArea); | 72 | this, "org.kdevelop.debugger.MemoryView", Qt::BottomDockWidgetArea); | ||
163 | core()->uiController()->addToolView( | 73 | core()->uiController()->addToolView( | ||
164 | i18n("Memory"), | 74 | i18n("Memory"), | ||
165 | memoryviewerfactory); | 75 | memoryviewerfactory); | ||
166 | #endif | 76 | #endif | ||
167 | 77 | | |||
168 | setupActions(); | | |||
169 | | ||||
170 | setupDBus(); | | |||
171 | | ||||
172 | QList<IPlugin*> plugins = KDevelop::ICore::self()->pluginController()->allPluginsForExtension("org.kdevelop.IExecutePlugin"); | 78 | QList<IPlugin*> plugins = KDevelop::ICore::self()->pluginController()->allPluginsForExtension("org.kdevelop.IExecutePlugin"); | ||
173 | foreach(IPlugin* plugin, plugins) { | 79 | foreach(IPlugin* plugin, plugins) { | ||
174 | IExecutePlugin* iface = plugin->extension<IExecutePlugin>(); | 80 | IExecutePlugin* iface = plugin->extension<IExecutePlugin>(); | ||
175 | Q_ASSERT(iface); | 81 | Q_ASSERT(iface); | ||
176 | KDevelop::LaunchConfigurationType* type = core()->runController()->launchConfigurationTypeForId( iface->nativeAppConfigTypeId() ); | 82 | KDevelop::LaunchConfigurationType* type = core()->runController()->launchConfigurationTypeForId( iface->nativeAppConfigTypeId() ); | ||
177 | Q_ASSERT(type); | 83 | Q_ASSERT(type); | ||
178 | type->addLauncher( new GdbLauncher( this, iface ) ); | 84 | type->addLauncher( new GdbLauncher( this, iface ) ); | ||
179 | } | 85 | } | ||
180 | } | 86 | } | ||
181 | 87 | | |||
182 | void CppDebuggerPlugin::unload() | 88 | void CppDebuggerPlugin::unload() | ||
183 | { | 89 | { | ||
184 | core()->uiController()->removeToolView(disassemblefactory); | 90 | core()->uiController()->removeToolView(disassemblefactory); | ||
185 | core()->uiController()->removeToolView(gdbfactory); | 91 | core()->uiController()->removeToolView(gdbfactory); | ||
186 | core()->uiController()->removeToolView(memoryviewerfactory); | 92 | core()->uiController()->removeToolView(memoryviewerfactory); | ||
187 | } | 93 | } | ||
188 | 94 | | |||
189 | void CppDebuggerPlugin::setupActions() | | |||
190 | { | | |||
191 | KActionCollection* ac = actionCollection(); | | |||
192 | | ||||
193 | QAction * action = new QAction(QIcon::fromTheme("core"), i18n("Examine Core File..."), this); | | |||
194 | action->setToolTip( i18n("Examine core file") ); | | |||
195 | action->setWhatsThis( i18n("<b>Examine core file</b><p>" | | |||
196 | "This loads a core file, which is typically created " | | |||
197 | "after the application has crashed, e.g. with a " | | |||
198 | "segmentation fault. The core file contains an " | | |||
199 | "image of the program memory at the time it crashed, " | | |||
200 | "allowing you to do a post-mortem analysis.</p>") ); | | |||
201 | connect(action, &QAction::triggered, this, &CppDebuggerPlugin::slotExamineCore); | | |||
202 | ac->addAction("debug_core", action); | | |||
203 | | ||||
204 | #ifdef KDEV_ENABLE_GDB_ATTACH_DIALOG | | |||
205 | action = new QAction(QIcon::fromTheme("connect_creating"), i18n("Attach to Process"), this); | | |||
206 | action->setToolTip( i18n("Attach to process...") ); | | |||
207 | action->setWhatsThis(i18n("<b>Attach to process</b><p>Attaches the debugger to a running process.</p>")); | | |||
208 | connect(action, SIGNAL(triggered(bool)), this, SLOT(slotAttachProcess())); | | |||
209 | ac->addAction("debug_attach", action); | | |||
210 | #endif | | |||
211 | } | | |||
212 | | ||||
213 | void CppDebuggerPlugin::setupDBus() | | |||
214 | { | | |||
215 | m_drkonqiMap = new QSignalMapper(this); | | |||
216 | connect(m_drkonqiMap, static_cast<void(QSignalMapper::*)(QObject*)>(&QSignalMapper::mapped), this, &CppDebuggerPlugin::slotDebugExternalProcess); | | |||
217 | | ||||
218 | QDBusConnectionInterface* dbusInterface = QDBusConnection::sessionBus().interface(); | | |||
219 | foreach (const QString& service, dbusInterface->registeredServiceNames().value()) | | |||
220 | slotDBusServiceRegistered(service); | | |||
221 | | ||||
222 | QDBusServiceWatcher* watcher = new QDBusServiceWatcher(this); | | |||
223 | connect(watcher, &QDBusServiceWatcher::serviceRegistered, | | |||
224 | this, &CppDebuggerPlugin::slotDBusServiceRegistered); | | |||
225 | connect(watcher, &QDBusServiceWatcher::serviceUnregistered, | | |||
226 | this, &CppDebuggerPlugin::slotDBusServiceUnregistered); | | |||
227 | } | | |||
228 | | ||||
229 | void CppDebuggerPlugin::slotDBusServiceRegistered( const QString& service ) | | |||
230 | { | | |||
231 | if (service.startsWith("org.kde.drkonqi")) { | | |||
232 | // New registration | | |||
233 | QDBusInterface* drkonqiInterface = new QDBusInterface(service, "/krashinfo", QString(), QDBusConnection::sessionBus(), this); | | |||
234 | m_drkonqis.insert(service, drkonqiInterface); | | |||
235 | | ||||
236 | connect(drkonqiInterface, SIGNAL(acceptDebuggingApplication()), m_drkonqiMap, SLOT(map())); | | |||
237 | m_drkonqiMap->setMapping(drkonqiInterface, drkonqiInterface); | | |||
238 | | ||||
239 | drkonqiInterface->call("registerDebuggingApplication", i18n("KDevelop")); | | |||
240 | } | | |||
241 | } | | |||
242 | | ||||
243 | void CppDebuggerPlugin::slotDBusServiceUnregistered( const QString& service ) | | |||
244 | { | | |||
245 | if (service.startsWith("org.kde.drkonqi")) { | | |||
246 | // Deregistration | | |||
247 | if (m_drkonqis.contains(service)) | | |||
248 | delete m_drkonqis.take(service); | | |||
249 | } | | |||
250 | } | | |||
251 | | ||||
252 | void CppDebuggerPlugin::slotDebugExternalProcess(QObject* interface) | | |||
253 | { | | |||
254 | QDBusReply<int> reply = static_cast<QDBusInterface*>(interface)->call("pid"); | | |||
255 | | ||||
256 | if (reply.isValid()) { | | |||
257 | attachProcess(reply.value()); | | |||
258 | QTimer::singleShot(500, this, SLOT(slotCloseDrKonqi())); | | |||
259 | | ||||
260 | m_drkonqi = m_drkonqis.key(static_cast<QDBusInterface*>(interface)); | | |||
261 | } | | |||
262 | | ||||
263 | KDevelop::ICore::self()->uiController()->activeMainWindow()->raise(); | | |||
264 | } | | |||
265 | | ||||
266 | void CppDebuggerPlugin::slotCloseDrKonqi() | | |||
267 | { | | |||
268 | if (!m_drkonqi.isEmpty()) { | | |||
269 | QDBusInterface drkonqiInterface(m_drkonqi, "/MainApplication", "org.kde.KApplication"); | | |||
270 | drkonqiInterface.call("quit"); | | |||
271 | m_drkonqi.clear(); | | |||
272 | } | | |||
273 | } | | |||
274 | | ||||
275 | CppDebuggerPlugin::~CppDebuggerPlugin() | 95 | CppDebuggerPlugin::~CppDebuggerPlugin() | ||
276 | { | 96 | { | ||
277 | } | 97 | } | ||
278 | 98 | | |||
279 | 99 | DebugSession* CppDebuggerPlugin::createSession() const | |||
280 | void CppDebuggerPlugin::initializeGuiState() | | |||
281 | { | | |||
282 | } | | |||
283 | | ||||
284 | KDevelop::ContextMenuExtension CppDebuggerPlugin::contextMenuExtension( KDevelop::Context* context ) | | |||
285 | { | | |||
286 | KDevelop::ContextMenuExtension menuExt = KDevelop::IPlugin::contextMenuExtension( context ); | | |||
287 | | ||||
288 | if( context->type() != KDevelop::Context::EditorContext ) | | |||
289 | return menuExt; | | |||
290 | | ||||
291 | KDevelop::EditorContext *econtext = dynamic_cast<KDevelop::EditorContext*>(context); | | |||
292 | if (!econtext) | | |||
293 | return menuExt; | | |||
294 | | ||||
295 | m_contextIdent = econtext->currentWord(); | | |||
296 | | ||||
297 | if (!m_contextIdent.isEmpty()) | | |||
298 | { | | |||
299 | // PORTING TODO | | |||
300 | //QString squeezed = KStringHandler::csqueeze(m_contextIdent, 30); | | |||
301 | QAction* action = new QAction( i18n("Evaluate: %1", m_contextIdent), this); | | |||
302 | connect(action, &QAction::triggered, this, &CppDebuggerPlugin::contextEvaluate); | | |||
303 | action->setWhatsThis(i18n("<b>Evaluate expression</b><p>Shows the value of the expression under the cursor.</p>")); | | |||
304 | menuExt.addAction( KDevelop::ContextMenuExtension::DebugGroup, action); | | |||
305 | | ||||
306 | action = new QAction( i18n("Watch: %1", m_contextIdent), this); | | |||
307 | connect(action, &QAction::triggered, this, &CppDebuggerPlugin::contextWatch); | | |||
308 | action->setWhatsThis(i18n("<b>Watch expression</b><p>Adds an expression under the cursor to the Variables/Watch list.</p>")); | | |||
309 | menuExt.addAction( KDevelop::ContextMenuExtension::DebugGroup, action); | | |||
310 | } | | |||
311 | | ||||
312 | return menuExt; | | |||
313 | } | | |||
314 | | ||||
315 | void CppDebuggerPlugin::contextWatch() | | |||
316 | { | | |||
317 | emit addWatchVariable(m_contextIdent); | | |||
318 | } | | |||
319 | | ||||
320 | void CppDebuggerPlugin::contextEvaluate() | | |||
321 | { | | |||
322 | emit evaluateExpression(m_contextIdent); | | |||
323 | } | | |||
324 | | ||||
325 | DebugSession* CppDebuggerPlugin::createSession() | | |||
326 | { | 100 | { | ||
327 | DebugSession *session = new DebugSession(); | 101 | DebugSession *session = new DebugSession(); | ||
328 | KDevelop::ICore::self()->debugController()->addSession(session); | 102 | KDevelop::ICore::self()->debugController()->addSession(session); | ||
329 | connect(session, &DebugSession::showMessage, this, &CppDebuggerPlugin::controllerMessage); | 103 | connect(session, &DebugSession::showMessage, this, &CppDebuggerPlugin::showStatusMessage); | ||
330 | connect(session, &DebugSession::reset, this, &CppDebuggerPlugin::reset); | 104 | connect(session, &DebugSession::reset, this, &CppDebuggerPlugin::reset); | ||
331 | connect(session, &DebugSession::finished, this, &CppDebuggerPlugin::slotFinished); | | |||
332 | connect(session, &DebugSession::raiseDebuggerConsoleViews, | 105 | connect(session, &DebugSession::raiseDebuggerConsoleViews, | ||
333 | this, &CppDebuggerPlugin::raiseGdbConsoleViews); | 106 | this, &CppDebuggerPlugin::raiseDebuggerConsoleViews); | ||
334 | return session; | 107 | return session; | ||
335 | } | 108 | } | ||
336 | 109 | | |||
337 | void CppDebuggerPlugin::slotExamineCore() | | |||
338 | { | | |||
339 | emit showMessage(this, i18n("Choose a core file to examine..."), 1000); | | |||
340 | | ||||
341 | SelectCoreDialog dlg(KDevelop::ICore::self()->uiController()->activeMainWindow()); | | |||
342 | if (dlg.exec() == QDialog::Rejected) { | | |||
343 | return; | | |||
344 | } | | |||
345 | | ||||
346 | emit showMessage(this, i18n("Examining core file %1", dlg.core().toLocalFile()), 1000); | | |||
347 | | ||||
348 | DebugSession* session = createSession(); | | |||
349 | session->examineCoreFile(dlg.binary(), dlg.core()); | | |||
350 | | ||||
351 | KillSessionJob *job = new KillSessionJob(session); | | |||
352 | job->setObjectName(i18n("Debug core file")); | | |||
353 | core()->runController()->registerJob(job); | | |||
354 | job->start(); | | |||
355 | } | | |||
356 | | ||||
357 | #ifdef KDEV_ENABLE_GDB_ATTACH_DIALOG | | |||
358 | void CppDebuggerPlugin::slotAttachProcess() | | |||
359 | { | | |||
360 | emit showMessage(this, i18n("Choose a process to attach to..."), 1000); | | |||
361 | | ||||
362 | ProcessSelectionDialog dlg; | | |||
363 | if (!dlg.exec() || !dlg.pidSelected()) | | |||
364 | return; | | |||
365 | | ||||
366 | int pid = dlg.pidSelected(); | | |||
367 | if(QApplication::applicationPid()==pid) | | |||
368 | KMessageBox::error(KDevelop::ICore::self()->uiController()->activeMainWindow(), | | |||
369 | i18n("Not attaching to process %1: cannot attach the debugger to itself.", pid)); | | |||
370 | else | | |||
371 | attachProcess(pid); | | |||
372 | } | | |||
373 | #endif | | |||
374 | | ||||
375 | void CppDebuggerPlugin::attachProcess(int pid) | | |||
376 | { | | |||
377 | emit showMessage(this, i18n("Attaching to process %1", pid), 1000); | | |||
378 | | ||||
379 | DebugSession* session = createSession(); | | |||
380 | session->attachToProcess(pid); | | |||
381 | | ||||
382 | KillSessionJob *job = new KillSessionJob(session); | | |||
383 | job->setObjectName(i18n("Debug process %1", pid)); | | |||
384 | core()->runController()->registerJob(job); | | |||
385 | job->start(); | | |||
386 | } | | |||
387 | | ||||
388 | // Used to disable breakpoint actions when non-text document selected | | |||
389 | | ||||
390 | // save/restore partial project session | | |||
391 | | ||||
392 | KConfigGroup CppDebuggerPlugin::config() const | | |||
393 | { | | |||
394 | return m_config; | | |||
395 | } | | |||
396 | | ||||
397 | QString CppDebuggerPlugin::statusName() const | | |||
398 | { | | |||
399 | return i18n("Debugger"); | | |||
400 | } | | |||
401 | | ||||
402 | void CppDebuggerPlugin::slotFinished() | | |||
403 | { | | |||
404 | /* TODO: is this required? | | |||
405 | Q_ASSERT(dynamic_cast<DebugSession*>(sender())); | | |||
406 | DebugSession* session = static_cast<DebugSession*>(sender()); | | |||
407 | */ | | |||
408 | } | | |||
409 | | ||||
410 | void CppDebuggerPlugin::controllerMessage( const QString& msg, int timeout ) | | |||
411 | { | | |||
412 | emit showMessage(this, msg, timeout); | | |||
413 | } | | |||
414 | | ||||
415 | #include "debuggerplugin.moc" | 110 | #include "debuggerplugin.moc" |