1 /*
2 * This file is part of the Code::Blocks IDE and licensed under the GNU General Public License, version 3
3 * http://www.gnu.org/licenses/gpl-3.0.html
4 *
5 * $Revision: 11639 $
6 * $Id: debuggermenu.cpp 11639 2019-04-20 16:58:16Z fuscated $
7 * $HeadURL: svn://svn.code.sf.net/p/codeblocks/code/branches/release-20.xx/src/src/debuggermenu.cpp $
8 */
9 #include "sdk.h"
10
11 #ifndef CB_PRECOMP
12 #include <wx/menu.h>
13 #include <wx/toolbar.h>
14 #include <wx/xrc/xmlres.h>
15
16 #include "cbeditor.h"
17 #include "cbproject.h"
18 #include "editormanager.h"
19 #include "logmanager.h"
20 #include "projectmanager.h"
21 #endif
22
23 #include "debuggermenu.h"
24
25 #include <algorithm>
26 #include <wx/aui/aui.h> // wxAuiManager
27
28 #include "cbdebugger_interfaces.h"
29 #include "cbstyledtextctrl.h"
30 #include "debuggermanager.h"
31
32 namespace
33 {
34 const int idMenuDebug = XRCID("idDebuggerMenuDebug");
35 const int idMenuDebugActive = XRCID("idDebuggerMenuActive");
36 const int idMenuDebugActiveTargetsDefault = wxNewId();
37 const int idMenuRunToCursor = XRCID("idDebuggerMenuRunToCursor");
38 const int idMenuSetNextStatement = XRCID("idDebuggerMenuSetNextStatement");
39 const int idMenuNext = XRCID("idDebuggerMenuNext");
40 const int idMenuStep = XRCID("idDebuggerMenuStep");
41 const int idMenuNextInstr = XRCID("idDebuggerMenuNextInstr");
42 const int idMenuStepIntoInstr = XRCID("idDebuggerMenuStepIntoInstr");
43 const int idMenuStepOut = XRCID("idDebuggerMenuStepOut");
44 const int idMenuBreak = XRCID("idDebuggerMenuBreak");
45 const int idMenuStop = XRCID("idDebuggerMenuStop");
46 const int idToolbarStop = XRCID("idDebuggerToolbarStop");
47 const int idMenuToggleBreakpoint = XRCID("idDebuggerMenuToggleBreakpoint");
48 const int idMenuRemoveAllBreakpoints = XRCID("idDebuggerMenuRemoveAllBreakpoints");
49 const int idMenuAddDataBreakpoint = XRCID("idMenuAddDataBreakpoint");
50 const int idMenuSendCommand = XRCID("idDebuggerMenuSendCommand");
51 const int idMenuAddSymbolFile = XRCID("idDebuggerMenuAddSymbolFile");
52 const int idMenuAttachToProcess = XRCID("idDebuggerMenuAttachToProcess");
53 const int idMenuDetach = XRCID("idDebuggerMenuDetach");
54
55 const long idMenuDebuggingWindows = XRCID("idDebuggingWindows");
56 const long idMenuTools = XRCID("idDebuggerInfo");
57
58 const int idDebuggerToolInfo = XRCID("idDebuggerToolInfo");
59 const int idDebuggerToolWindows = XRCID("idDebuggerToolWindows");
60
61 const int idMenuDebuggerAddWatch = wxNewId();
62
HideValueTooltip()63 inline void HideValueTooltip() { Manager::Get()->GetDebuggerManager()->GetInterfaceFactory()->HideValueTooltip(); }
64
Support(cbDebuggerPlugin * plugin,cbDebuggerFeature::Flags flag)65 bool Support(cbDebuggerPlugin *plugin, cbDebuggerFeature::Flags flag)
66 {
67 return plugin && plugin->SupportsFeature(flag);
68 }
69
GetMenuById(long menuId,bool recreate=false)70 wxMenu* GetMenuById(long menuId, bool recreate = false)
71 {
72 wxMenuBar* mbar = Manager::Get()->GetAppFrame()->GetMenuBar();
73 if (!mbar)
74 return nullptr;
75 wxMenuItem *item = mbar->FindItem(menuId);
76 if (!item)
77 return nullptr;
78 if (recreate)
79 {
80 wxMenu *menu = item->GetMenu();
81 int pos = wxNOT_FOUND;
82 for (size_t ii = 0; ii < menu->GetMenuItemCount(); ++ii)
83 {
84 if (item == menu->FindItemByPosition(ii))
85 {
86 pos = ii;
87 break;
88 }
89 }
90 if (pos != wxNOT_FOUND)
91 {
92 wxMenu *newSubMenu = new wxMenu;
93 wxMenuItem *newItem = new wxMenuItem(menu, item->GetId(), item->GetItemLabelText(), item->GetHelp(),
94 item->GetKind(), newSubMenu);
95 menu->Insert(pos, newItem);
96
97 menu->Destroy(item);
98 return newItem->GetSubMenu();
99 }
100 }
101 return item ? item->GetSubMenu() : nullptr;
102 }
103 }
104
BEGIN_EVENT_TABLE(DebuggerMenuHandler,wxEvtHandler)105 BEGIN_EVENT_TABLE(DebuggerMenuHandler, wxEvtHandler)
106 // these are different because they are loaded from the XRC
107 EVT_UPDATE_UI(idMenuDebug, DebuggerMenuHandler::OnUpdateUI)
108 EVT_UPDATE_UI(idMenuRunToCursor, DebuggerMenuHandler::OnUpdateUI)
109 EVT_UPDATE_UI(idMenuSetNextStatement, DebuggerMenuHandler::OnUpdateUI)
110 EVT_UPDATE_UI(idMenuNext, DebuggerMenuHandler::OnUpdateUI)
111 EVT_UPDATE_UI(idMenuNextInstr, DebuggerMenuHandler::OnUpdateUI)
112 EVT_UPDATE_UI(idMenuStepIntoInstr, DebuggerMenuHandler::OnUpdateUI)
113 EVT_UPDATE_UI(idMenuStep, DebuggerMenuHandler::OnUpdateUI)
114 EVT_UPDATE_UI(idMenuStepOut, DebuggerMenuHandler::OnUpdateUI)
115 EVT_UPDATE_UI(idMenuBreak, DebuggerMenuHandler::OnUpdateUI)
116 EVT_UPDATE_UI(idMenuStop, DebuggerMenuHandler::OnUpdateUI)
117
118 EVT_UPDATE_UI(idMenuAttachToProcess, DebuggerMenuHandler::OnUpdateUI)
119 EVT_UPDATE_UI(idMenuDetach, DebuggerMenuHandler::OnUpdateUI)
120
121 EVT_UPDATE_UI(idMenuTools, DebuggerMenuHandler::OnUpdateUI)
122
123 EVT_MENU(idMenuDebug, DebuggerMenuHandler::OnStart)
124 EVT_MENU(idMenuBreak, DebuggerMenuHandler::OnBreak)
125 EVT_MENU(idMenuStop, DebuggerMenuHandler::OnStop)
126 EVT_MENU(idMenuNext, DebuggerMenuHandler::OnNext)
127 EVT_MENU(idMenuStep, DebuggerMenuHandler::OnStep)
128 EVT_MENU(idMenuNextInstr, DebuggerMenuHandler::OnNextInstr)
129 EVT_MENU(idMenuStepIntoInstr, DebuggerMenuHandler::OnStepIntoInstr)
130 EVT_MENU(idMenuStepOut, DebuggerMenuHandler::OnStepOut)
131 EVT_MENU(idMenuRunToCursor, DebuggerMenuHandler::OnRunToCursor)
132 EVT_MENU(idMenuSetNextStatement, DebuggerMenuHandler::OnSetNextStatement)
133 EVT_MENU(idMenuToggleBreakpoint, DebuggerMenuHandler::OnToggleBreakpoint)
134 EVT_MENU(idMenuRemoveAllBreakpoints, DebuggerMenuHandler::OnRemoveAllBreakpoints)
135 EVT_MENU(idMenuAddDataBreakpoint, DebuggerMenuHandler::OnAddDataBreakpoint)
136 EVT_MENU(idMenuSendCommand, DebuggerMenuHandler::OnSendCommand)
137
138 EVT_MENU(idMenuDebuggerAddWatch, DebuggerMenuHandler::OnAddWatch)
139 EVT_MENU(idMenuAttachToProcess, DebuggerMenuHandler::OnAttachToProcess)
140 EVT_MENU(idMenuDetach, DebuggerMenuHandler::OnDetachFromProcess)
141 EVT_MENU(idMenuDebugActiveTargetsDefault, DebuggerMenuHandler::OnActiveDebuggerTargetsDefaultClick)
142 END_EVENT_TABLE()
143
144
145 DebuggerMenuHandler::DebuggerMenuHandler() :
146 m_activeDebugger(nullptr),
147 m_disableContinue(false)
148 {
149 }
150
151 namespace
152 {
153 template<typename DlgType>
154 struct CommonItem : cbDebuggerWindowMenuItem
155 {
156 typedef DlgType* (DebuggerManager::*GetWindowFunc)();
CommonItem__anon1f21c0d20211::CommonItem157 CommonItem(cbDebuggerFeature::Flags enableFeature, cbDebuggerPlugin::DebugWindows requestUpdate, GetWindowFunc func) :
158 m_enableFeature(enableFeature),
159 m_requestUpdate(requestUpdate),
160 m_getWindowFunc(func)
161 {}
162
OnClick__anon1f21c0d20211::CommonItem163 void OnClick(bool enable) override
164 {
165 CodeBlocksDockEvent evt(enable ? cbEVT_SHOW_DOCK_WINDOW : cbEVT_HIDE_DOCK_WINDOW);
166 DebuggerManager *manager = Manager::Get()->GetDebuggerManager();
167 DlgType *dialog = (manager->*(m_getWindowFunc))();
168 if (dialog)
169 {
170 evt.pWindow = dialog->GetWindow();
171 Manager::Get()->ProcessEvent(evt);
172 }
173
174 if (enable && manager->GetActiveDebugger())
175 manager->GetActiveDebugger()->RequestUpdate(m_requestUpdate);
176 }
IsEnabled__anon1f21c0d20211::CommonItem177 bool IsEnabled() override
178 {
179 return Support(Manager::Get()->GetDebuggerManager()->GetActiveDebugger(), m_enableFeature);
180 }
IsChecked__anon1f21c0d20211::CommonItem181 bool IsChecked() override
182 {
183 DlgType *dialog = (Manager::Get()->GetDebuggerManager()->*(m_getWindowFunc))();
184 return dialog && IsWindowReallyShown(dialog->GetWindow());
185 }
186 private:
187 cbDebuggerFeature::Flags m_enableFeature;
188 cbDebuggerPlugin::DebugWindows m_requestUpdate;
189 GetWindowFunc m_getWindowFunc;
190 };
191
192 template<typename DlgType>
MakeItem(cbDebuggerFeature::Flags enableFeature,cbDebuggerPlugin::DebugWindows requestUpdate,DlgType * (DebuggerManager::* func)())193 CommonItem<DlgType>* MakeItem(cbDebuggerFeature::Flags enableFeature,
194 cbDebuggerPlugin::DebugWindows requestUpdate,
195 DlgType* (DebuggerManager::*func)())
196 {
197 return new CommonItem<DlgType>(enableFeature, requestUpdate, func);
198 }
199
200 } // anonymous namespace
201
RegisterDefaultWindowItems()202 void DebuggerMenuHandler::RegisterDefaultWindowItems()
203 {
204 struct Breakpoints : cbDebuggerWindowMenuItem
205 {
206 void OnClick(bool enable) override
207 {
208 CodeBlocksDockEvent evt(enable ? cbEVT_SHOW_DOCK_WINDOW : cbEVT_HIDE_DOCK_WINDOW);
209 cbBreakpointsDlg *dialog = Manager::Get()->GetDebuggerManager()->GetBreakpointDialog();
210 if (dialog)
211 {
212 evt.pWindow = dialog->GetWindow();
213 Manager::Get()->ProcessEvent(evt);
214 }
215 }
216 bool IsEnabled() override
217 {
218 return Manager::Get()->GetDebuggerManager()->GetBreakpointDialog();
219 }
220 bool IsChecked() override
221 {
222 cbBreakpointsDlg *dialog = Manager::Get()->GetDebuggerManager()->GetBreakpointDialog();
223 return dialog && IsWindowReallyShown(dialog->GetWindow());
224 }
225 };
226 struct Watches : CommonItem<cbWatchesDlg>
227 {
228 Watches() :
229 CommonItem<cbWatchesDlg>(cbDebuggerFeature::Watches, cbDebuggerPlugin::Watches, &DebuggerManager::GetWatchesDialog)
230 {
231 }
232 bool IsEnabled() override
233 {
234 return Manager::Get()->GetDebuggerManager()->GetWatchesDialog();
235 }
236 };
237
238 RegisterWindowMenu(_("Breakpoints"), _("Edit breakpoints"), new Breakpoints);
239 RegisterWindowMenu(_("Watches"), _("Watch variables"), new Watches);
240 RegisterWindowMenu(_("Call stack"), _("Displays the current call stack"),
241 MakeItem(cbDebuggerFeature::Callstack, cbDebuggerPlugin::Backtrace,
242 &DebuggerManager::GetBacktraceDialog));
243 RegisterWindowMenu(_("CPU Registers"), _("Display the CPU registers"),
244 MakeItem(cbDebuggerFeature::CPURegisters, cbDebuggerPlugin::CPURegisters,
245 &DebuggerManager::GetCPURegistersDialog));
246 RegisterWindowMenu(_("Disassembly"), _("Disassembles the current stack frame"),
247 MakeItem(cbDebuggerFeature::Disassembly, cbDebuggerPlugin::Disassembly,
248 &DebuggerManager::GetDisassemblyDialog));
249 RegisterWindowMenu(_("Memory dump"), _("Displays the contents of a memory location"),
250 MakeItem(cbDebuggerFeature::ExamineMemory, cbDebuggerPlugin::ExamineMemory,
251 &DebuggerManager::GetExamineMemoryDialog));
252 RegisterWindowMenu(_("Running threads"),
253 _("Displays the currently running threads and allows switching between them"),
254 MakeItem(cbDebuggerFeature::Threads, cbDebuggerPlugin::Threads,
255 &DebuggerManager::GetThreadsDialog));
256 }
257
RegisterWindowMenu(const wxString & name,const wxString & help,cbDebuggerWindowMenuItem * item)258 bool DebuggerMenuHandler::RegisterWindowMenu(const wxString &name, const wxString &help, cbDebuggerWindowMenuItem *item)
259 {
260 for (WindowMenuItemsMap::iterator it = m_windowMenuItems.begin(); it != m_windowMenuItems.end(); ++it)
261 {
262 if (it->second.name == name)
263 {
264 wxString msg = wxString::Format(_("Duplicate debugger window name '%s'. Igrnoring it."), name.wx_str());
265 Manager::Get()->GetLogManager()->DebugLog(msg, Logger::error);
266 delete item;
267 return false;
268 }
269 }
270
271 WindowMenuItem i;
272 i.item = cb::shared_ptr<cbDebuggerWindowMenuItem>(item);
273 i.name = name;
274 i.help = help;
275 long id = wxNewId();
276
277 m_windowMenuItems[id] = i;
278
279 Connect(id, wxEVT_UPDATE_UI, wxObjectEventFunction(&DebuggerMenuHandler::OnWindowMenuItemUpdateUI));
280 Connect(id, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(DebuggerMenuHandler::OnWindowMenuItemClicked));
281 return true;
282 }
283
UnregisterWindowMenu(const wxString & name)284 void DebuggerMenuHandler::UnregisterWindowMenu(const wxString &name)
285 {
286 for (WindowMenuItemsMap::iterator it = m_windowMenuItems.begin(); it != m_windowMenuItems.end(); ++it)
287 {
288 if (it->second.name == name)
289 {
290 Disconnect(it->first, wxEVT_UPDATE_UI);
291 Disconnect(it->first, wxEVT_COMMAND_MENU_SELECTED);
292
293 m_windowMenuItems.erase(it);
294 return;
295 }
296 }
297 }
298
OnWindowMenuItemUpdateUI(wxUpdateUIEvent & event)299 void DebuggerMenuHandler::OnWindowMenuItemUpdateUI(wxUpdateUIEvent& event)
300 {
301 WindowMenuItemsMap::iterator it = m_windowMenuItems.find(event.GetId());
302 if (it != m_windowMenuItems.end())
303 {
304 event.Check(it->second.item->IsChecked());
305 event.Enable(it->second.item->IsEnabled());
306 }
307 }
308
OnWindowMenuItemClicked(wxCommandEvent & event)309 void DebuggerMenuHandler::OnWindowMenuItemClicked(wxCommandEvent &event)
310 {
311 WindowMenuItemsMap::iterator it = m_windowMenuItems.find(event.GetId());
312 if (it != m_windowMenuItems.end())
313 it->second.item->OnClick(event.IsChecked());
314 }
315
AppendWindowMenuItems(wxMenu & menu)316 void DebuggerMenuHandler::AppendWindowMenuItems(wxMenu &menu)
317 {
318 std::map<wxString, long> sortedNames;
319
320 for (WindowMenuItemsMap::iterator it = m_windowMenuItems.begin(); it != m_windowMenuItems.end(); ++it)
321 sortedNames[it->second.name] = it->first;
322
323 for (std::map<wxString, long>::iterator it = sortedNames.begin(); it != sortedNames.end(); ++it)
324 menu.AppendCheckItem(it->second, it->first, m_windowMenuItems[it->second].help);
325 }
326
SetActiveDebugger(cbDebuggerPlugin * active)327 void DebuggerMenuHandler::SetActiveDebugger(cbDebuggerPlugin *active)
328 {
329 m_activeDebugger = active;
330 }
331
MarkActiveTargetAsValid(bool valid)332 void DebuggerMenuHandler::MarkActiveTargetAsValid(bool valid)
333 {
334 wxMenu *menu = GetMenuById(idMenuDebugActive);
335 if (!menu)
336 return;
337 wxMenuItem *item = menu->FindItem(idMenuDebugActiveTargetsDefault);
338 if (item)
339 #if wxCHECK_VERSION(3, 0, 0)
340 item->SetItemLabel(valid ? _("Target's default") : _("Target's default (invalid)"));
341 #else
342 item->SetText(valid ? _("Target's default") : _("Target's default (invalid)"));
343 #endif
344 }
345
RebuildMenus()346 void DebuggerMenuHandler::RebuildMenus()
347 {
348 wxMenu *menuWindows = GetMenuById(idMenuDebuggingWindows, true);
349 if (menuWindows)
350 AppendWindowMenuItems(*menuWindows);
351 if (m_activeDebugger)
352 {
353 wxMenu *menuTools = GetMenuById(idMenuTools, true);
354 if (menuTools)
355 m_activeDebugger->SetupToolsMenu(*menuTools);
356 }
357
358 DebuggerManager *dbgManager = Manager::Get()->GetDebuggerManager();
359 wxMenu *menu = GetMenuById(idMenuDebugActive, true);
360 if (!menu)
361 return;
362
363 menu->AppendRadioItem(idMenuDebugActiveTargetsDefault, _("Target's default"),
364 _("Use the debugger associated with the compiler for the active target"));
365
366 const DebuggerManager::RegisteredPlugins &allDebugger = dbgManager->GetAllDebuggers();
367 for (DebuggerManager::RegisteredPlugins::const_iterator it = allDebugger.begin(); it != allDebugger.end(); ++it)
368 {
369 const DebuggerManager::ConfigurationVector &configs = it->second.GetConfigurations();
370 for (DebuggerManager::ConfigurationVector::const_iterator itConf = configs.begin(); itConf != configs.end(); ++itConf)
371 {
372 long id = (*itConf)->GetMenuId();
373 if (id == wxID_ANY)
374 {
375 id = wxNewId();
376 (*itConf)->SetMenuId(id);
377 }
378
379 menu->AppendRadioItem(id, it->first->GetGUIName() + wxT(": ") + (*itConf)->GetName());
380 Connect(id, -1, wxEVT_COMMAND_MENU_SELECTED,
381 (wxObjectEventFunction) (wxEventFunction) (wxCommandEventFunction)
382 &DebuggerMenuHandler::OnActiveDebuggerClick);
383 }
384 }
385
386 if (m_activeDebugger && !dbgManager->IsActiveDebuggerTargetsDefault())
387 {
388 DebuggerManager::RegisteredPlugins::const_iterator it = allDebugger.find(m_activeDebugger);
389 cbAssert(it != allDebugger.end());
390
391 const DebuggerManager::ConfigurationVector &configs = it->second.GetConfigurations();
392
393 DebuggerManager::ConfigurationVector::const_iterator itConf = configs.begin();
394 std::advance(itConf, m_activeDebugger->GetIndexOfActiveConfig());
395
396 if (itConf != configs.end())
397 menu->Check((*itConf)->GetMenuId(), true);
398 else
399 menu->Check(configs.front()->GetMenuId(), true);
400 }
401 else
402 menu->Check(idMenuDebugActiveTargetsDefault, true);
403 }
404
BuildContextMenu(wxMenu & menu,const wxString & word_at_caret,bool is_running)405 void DebuggerMenuHandler::BuildContextMenu(wxMenu &menu, const wxString& word_at_caret, bool is_running)
406 {
407 cbDebuggerPlugin *plugin = Manager::Get()->GetDebuggerManager()->GetActiveDebugger();
408 if (!plugin)
409 return;
410
411 PluginManager *pluginManager = Manager::Get()->GetPluginManager();
412
413 int initialItem;
414 if (is_running)
415 {
416 // we want debugger menu items to be at the top when debugging
417 initialItem = pluginManager->GetFindMenuItemFirst();
418 }
419 else
420 initialItem = pluginManager->GetFindMenuItemFirst() + pluginManager->GetFindMenuItemCount();
421 int item = initialItem;
422
423 // Insert Run to Cursor
424 if (plugin->SupportsFeature(cbDebuggerFeature::RunToCursor))
425 menu.Insert(item++, idMenuRunToCursor, _("Run to cursor"));
426 if (is_running)
427 {
428 if (plugin->SupportsFeature(cbDebuggerFeature::SetNextStatement))
429 menu.Insert(item++, idMenuSetNextStatement, _("Set next statement"));
430 if (item > 0)
431 menu.InsertSeparator(item++);
432 if (!word_at_caret.empty())
433 {
434 if (plugin->SupportsFeature(cbDebuggerFeature::Watches))
435 menu.Insert(item++, idMenuDebuggerAddWatch, wxString::Format(_("Watch '%s'"), word_at_caret.c_str()));
436 // data breakpoint
437 if (plugin->SupportsFeature(cbDebuggerFeature::Breakpoints))
438 {
439 menu.Insert(item++, idMenuAddDataBreakpoint,
440 wxString::Format(_("Add data breakpoint for '%s'"), word_at_caret.c_str()));
441 }
442 }
443 }
444 // Insert toggle breakpoint
445 if (plugin->SupportsFeature(cbDebuggerFeature::Breakpoints))
446 menu.Insert(item++, idMenuToggleBreakpoint, _("Toggle breakpoint"));
447 if (item > 0)
448 menu.InsertSeparator(item++);
449
450 if (is_running)
451 pluginManager->RegisterFindMenuItems(true, item - initialItem);
452 }
453
OnUpdateUI(wxUpdateUIEvent & event)454 void DebuggerMenuHandler::OnUpdateUI(wxUpdateUIEvent& event)
455 {
456 cbProject* prj = Manager::Get()->GetProjectManager()->GetActiveProject();
457 bool en = false, stopped = false, isRunning = false, isAttached = false;
458
459 if (m_activeDebugger)
460 {
461 isAttached = m_activeDebugger->IsAttachedToProcess();
462 en = (prj && !prj->GetCurrentlyCompilingTarget()) || isAttached;
463 stopped = m_activeDebugger->IsStopped() && !m_activeDebugger->IsBusy();
464 isRunning = m_activeDebugger->IsRunning();
465 }
466
467 cbEditor* ed = Manager::Get()->GetEditorManager()->GetBuiltinActiveEditor();
468 wxMenuBar* mbar = Manager::Get()->GetAppFrame()->GetMenuBar();
469 cbPlugin *runningPlugin = Manager::Get()->GetProjectManager()->GetIsRunning();
470
471 bool otherPlugin = false;
472 if (runningPlugin != NULL && runningPlugin != m_activeDebugger)
473 {
474 en = false;
475 otherPlugin = true;
476 }
477
478 if (mbar && Manager::Get()->GetDebuggerManager()->HasMenu())
479 {
480 bool hasBreaks = Support(m_activeDebugger, cbDebuggerFeature::Breakpoints);
481
482 mbar->Enable(idMenuDebug, (!isRunning || stopped) && en);
483 mbar->Enable(idMenuNext, isRunning && en && stopped);
484 mbar->Enable(idMenuNextInstr, isRunning && en && stopped);
485 mbar->Enable(idMenuStepIntoInstr, isRunning && en && stopped);
486 mbar->Enable(idMenuStep, en && stopped);
487 mbar->Enable(idMenuStepOut, isRunning && en && stopped);
488 mbar->Enable(idMenuRunToCursor,
489 en && ed && stopped && Support(m_activeDebugger, cbDebuggerFeature::RunToCursor));
490 mbar->Enable(idMenuSetNextStatement,
491 en && ed && stopped && isRunning && Support(m_activeDebugger, cbDebuggerFeature::SetNextStatement));
492 mbar->Enable(idMenuToggleBreakpoint, ed && m_activeDebugger && hasBreaks);
493 mbar->Enable(idMenuRemoveAllBreakpoints, m_activeDebugger && hasBreaks);
494 mbar->Enable(idMenuSendCommand, isRunning && stopped);
495 mbar->Enable(idMenuAddSymbolFile, isRunning && stopped);
496 mbar->Enable(idMenuStop, isRunning && en);
497 mbar->Enable(idMenuBreak, isRunning && !stopped && en);
498 mbar->Enable(idMenuAttachToProcess, !isRunning && !otherPlugin && m_activeDebugger);
499 mbar->Enable(idMenuDetach, isRunning && stopped && isAttached);
500
501 wxMenu *activeMenu = GetMenuById(idMenuDebugActive);
502 if (activeMenu)
503 {
504 for (size_t ii = 0; ii < activeMenu->GetMenuItemCount(); ++ii)
505 activeMenu->Enable(activeMenu->FindItemByPosition(ii)->GetId(), !isRunning);
506 }
507
508 mbar->Enable(idMenuTools, m_activeDebugger && m_activeDebugger->ToolMenuEnabled());
509 }
510
511 // allow other UpdateUI handlers to process this event
512 // *very* important! don't forget it...
513 event.Skip();
514 }
515
LogActiveConfig()516 void DebuggerMenuHandler::LogActiveConfig()
517 {
518 DebuggerManager *dbgManager = Manager::Get()->GetDebuggerManager();
519 const DebuggerManager::RegisteredPlugins &allDebuggers = dbgManager->GetAllDebuggers();
520 DebuggerManager::RegisteredPlugins::const_iterator it = allDebuggers.find(m_activeDebugger);
521 wxString configName;
522 if (it != allDebuggers.end())
523 {
524 cbDebuggerConfiguration &config = m_activeDebugger->GetActiveConfig();
525 configName = it->first->GetGUIName() + wxT(":") + config.GetName();
526 }
527 m_activeDebugger->Log(_("Active debugger config: ") + configName);
528 }
529
OnStart(cb_unused wxCommandEvent & event)530 void DebuggerMenuHandler::OnStart(cb_unused wxCommandEvent& event)
531 {
532 cbAssert(m_activeDebugger);
533 if (!m_activeDebugger->IsRunning())
534 {
535 m_disableContinue = true;
536
537 ProjectManager *manager = Manager::Get()->GetProjectManager();
538 if (manager->GetIsRunning() == nullptr)
539 {
540 manager->SetIsRunning(m_activeDebugger);
541
542 m_activeDebugger->ClearLog();
543 LogActiveConfig();
544
545 if (!m_activeDebugger->Debug(false))
546 manager->SetIsRunning(nullptr);
547 }
548 m_disableContinue = false;
549 }
550 else if (m_activeDebugger->IsStopped() && !m_disableContinue)
551 {
552 HideValueTooltip();
553 m_activeDebugger->Continue();
554 }
555 }
556
OnBreak(cb_unused wxCommandEvent & event)557 void DebuggerMenuHandler::OnBreak(cb_unused wxCommandEvent& event)
558 {
559 cbAssert(m_activeDebugger);
560 HideValueTooltip();
561 m_activeDebugger->Break();
562 }
563
OnStop(cb_unused wxCommandEvent & event)564 void DebuggerMenuHandler::OnStop(cb_unused wxCommandEvent& event)
565 {
566 cbAssert(m_activeDebugger);
567 HideValueTooltip();
568 m_activeDebugger->Stop();
569 }
570
OnContinue(cb_unused wxCommandEvent & event)571 void DebuggerMenuHandler::OnContinue(cb_unused wxCommandEvent& event)
572 {
573 cbAssert(m_activeDebugger);
574 if(!m_disableContinue)
575 {
576 HideValueTooltip();
577 m_activeDebugger->Continue();
578 }
579 }
580
OnNext(cb_unused wxCommandEvent & event)581 void DebuggerMenuHandler::OnNext(cb_unused wxCommandEvent& event)
582 {
583 cbAssert(m_activeDebugger);
584 HideValueTooltip();
585 m_activeDebugger->Next();
586 }
587
OnNextInstr(cb_unused wxCommandEvent & event)588 void DebuggerMenuHandler::OnNextInstr(cb_unused wxCommandEvent& event)
589 {
590 cbAssert(m_activeDebugger);
591 HideValueTooltip();
592 m_activeDebugger->NextInstruction();
593 }
594
OnStepIntoInstr(cb_unused wxCommandEvent & event)595 void DebuggerMenuHandler::OnStepIntoInstr(cb_unused wxCommandEvent& event)
596 {
597 cbAssert(m_activeDebugger);
598 HideValueTooltip();
599 m_activeDebugger->StepIntoInstruction();
600 }
601
OnStep(cb_unused wxCommandEvent & event)602 void DebuggerMenuHandler::OnStep(cb_unused wxCommandEvent& event)
603 {
604 cbAssert(m_activeDebugger);
605 if (m_activeDebugger->IsRunning())
606 {
607 if(!m_disableContinue)
608 {
609 HideValueTooltip();
610 m_activeDebugger->Step();
611 }
612 }
613 else
614 {
615 m_disableContinue = true;
616 ProjectManager *manager = Manager::Get()->GetProjectManager();
617 if (manager->GetIsRunning() == nullptr)
618 {
619 manager->SetIsRunning(m_activeDebugger);
620 m_activeDebugger->ClearLog();
621 LogActiveConfig();
622
623 if (!m_activeDebugger->Debug(true))
624 manager->SetIsRunning(nullptr);
625 }
626 m_disableContinue = false;
627 }
628 }
629
OnStepOut(cb_unused wxCommandEvent & event)630 void DebuggerMenuHandler::OnStepOut(cb_unused wxCommandEvent& event)
631 {
632 cbAssert(m_activeDebugger);
633 HideValueTooltip();
634 m_activeDebugger->StepOut();
635 }
636
OnRunToCursor(cb_unused wxCommandEvent & event)637 void DebuggerMenuHandler::OnRunToCursor(cb_unused wxCommandEvent& event)
638 {
639 cbAssert(m_activeDebugger);
640 cbEditor* ed = Manager::Get()->GetEditorManager()->GetBuiltinActiveEditor();
641 if (!ed)
642 return;
643 const wxString &line_text = ed->GetControl()->GetLine(ed->GetControl()->GetCurrentLine());
644
645 ProjectManager *manager = Manager::Get()->GetProjectManager();
646 if (manager->GetIsRunning() == nullptr || manager->GetIsRunning() == m_activeDebugger)
647 {
648 manager->SetIsRunning(m_activeDebugger);
649 if (!m_activeDebugger->IsRunning())
650 {
651 m_activeDebugger->ClearLog();
652 LogActiveConfig();
653 }
654 HideValueTooltip();
655 if (!m_activeDebugger->RunToCursor(ed->GetFilename(), ed->GetControl()->GetCurrentLine() + 1, line_text))
656 manager->SetIsRunning(nullptr);
657 }
658 }
659
OnSetNextStatement(cb_unused wxCommandEvent & event)660 void DebuggerMenuHandler::OnSetNextStatement(cb_unused wxCommandEvent& event)
661 {
662 cbAssert(m_activeDebugger);
663 cbEditor* ed = Manager::Get()->GetEditorManager()->GetBuiltinActiveEditor();
664 if (!ed)
665 return;
666 HideValueTooltip();
667 m_activeDebugger->SetNextStatement(ed->GetFilename(), ed->GetControl()->GetCurrentLine() + 1);
668 }
669
OnToggleBreakpoint(cb_unused wxCommandEvent & event)670 void DebuggerMenuHandler::OnToggleBreakpoint(cb_unused wxCommandEvent& event)
671 {
672 cbAssert(m_activeDebugger);
673 cbEditor* ed = Manager::Get()->GetEditorManager()->GetBuiltinActiveEditor();
674 if (!ed)
675 return;
676 ed->ToggleBreakpoint();
677 }
678
OnRemoveAllBreakpoints(cb_unused wxCommandEvent & event)679 void DebuggerMenuHandler::OnRemoveAllBreakpoints(cb_unused wxCommandEvent& event)
680 {
681 cbAssert(m_activeDebugger);
682 Manager::Get()->GetDebuggerManager()->GetBreakpointDialog()->RemoveAllBreakpoints();
683 }
684
OnAddDataBreakpoint(cb_unused wxCommandEvent & event)685 void DebuggerMenuHandler::OnAddDataBreakpoint(cb_unused wxCommandEvent& event)
686 {
687 cbAssert(m_activeDebugger);
688 const wxString& word_at_caret = m_activeDebugger->GetEditorWordAtCaret();
689 if (!word_at_caret.empty())
690 {
691 if (m_activeDebugger->AddDataBreakpoint(word_at_caret))
692 Manager::Get()->GetDebuggerManager()->GetBreakpointDialog()->Reload();
693 }
694 }
695
OnAttachToProcess(cb_unused wxCommandEvent & event)696 void DebuggerMenuHandler::OnAttachToProcess(cb_unused wxCommandEvent& event)
697 {
698 cbAssert(m_activeDebugger);
699 wxString pidStr = cbGetTextFromUser(_("PID to attach to:"));
700 if (!pidStr.empty())
701 {
702 m_activeDebugger->AttachToProcess(pidStr);
703 }
704 }
705
OnDetachFromProcess(cb_unused wxCommandEvent & event)706 void DebuggerMenuHandler::OnDetachFromProcess(cb_unused wxCommandEvent& event)
707 {
708 cbAssert(m_activeDebugger);
709 m_activeDebugger->DetachFromProcess();
710 }
711
OnSendCommand(cb_unused wxCommandEvent & event)712 void DebuggerMenuHandler::OnSendCommand(cb_unused wxCommandEvent& event)
713 {
714 cbAssert(m_activeDebugger);
715 wxString cmd = cbGetTextFromUser(_("Enter command for Debugger:"), _("Send command to Debugger:"), m_lastCommand);
716 if (cmd.IsEmpty())
717 return;
718
719 m_activeDebugger->SendCommand(cmd, false);
720 m_lastCommand = cmd;
721 }
722
OnAddWatch(cb_unused wxCommandEvent & event)723 void DebuggerMenuHandler::OnAddWatch(cb_unused wxCommandEvent& event)
724 {
725 if (!m_activeDebugger)
726 return;
727
728 wxString const &src = m_activeDebugger->GetEditorWordAtCaret();
729 if (!src.empty())
730 {
731 cb::shared_ptr<cbWatch> watch = m_activeDebugger->AddWatch(src, true);
732 if (watch.get())
733 {
734 cbWatchesDlg *dialog = Manager::Get()->GetDebuggerManager()->GetWatchesDialog();
735 dialog->AddWatch(watch);
736 if (!IsWindowReallyShown(dialog->GetWindow()))
737 {
738 CodeBlocksDockEvent evt(cbEVT_SHOW_DOCK_WINDOW);
739 evt.pWindow = dialog->GetWindow();
740 Manager::Get()->ProcessEvent(evt);
741 }
742 }
743 }
744 }
745
OnActiveDebuggerClick(wxCommandEvent & event)746 void DebuggerMenuHandler::OnActiveDebuggerClick(wxCommandEvent& event)
747 {
748 DebuggerManager *manager = Manager::Get()->GetDebuggerManager();
749 const DebuggerManager::RegisteredPlugins &plugins = manager->GetAllDebuggers();
750
751 for(DebuggerManager::RegisteredPlugins::const_iterator it = plugins.begin(); it != plugins.end(); ++it)
752 {
753 const DebuggerManager::ConfigurationVector &configs = it->second.GetConfigurations();
754 for (DebuggerManager::ConfigurationVector::const_iterator itConf = configs.begin(); itConf != configs.end(); ++itConf)
755 {
756 if((*itConf)->GetMenuId() == event.GetId())
757 {
758 manager->SetActiveDebugger(it->first, itConf);
759 return;
760 }
761 }
762 }
763 }
764
OnActiveDebuggerTargetsDefaultClick(cb_unused wxCommandEvent & event)765 void DebuggerMenuHandler::OnActiveDebuggerTargetsDefaultClick(cb_unused wxCommandEvent& event)
766 {
767 Manager::Get()->GetDebuggerManager()->SetTargetsDefaultAsActiveDebugger();
768 }
769
770
BEGIN_EVENT_TABLE(DebuggerToolbarHandler,wxEvtHandler)771 BEGIN_EVENT_TABLE(DebuggerToolbarHandler, wxEvtHandler)
772 // these are different because they are loaded from the XRC
773 EVT_UPDATE_UI(idMenuDebug, DebuggerToolbarHandler::OnUpdateUI)
774 EVT_UPDATE_UI(idMenuRunToCursor, DebuggerToolbarHandler::OnUpdateUI)
775 EVT_UPDATE_UI(idMenuNext, DebuggerToolbarHandler::OnUpdateUI)
776 EVT_UPDATE_UI(idMenuNextInstr, DebuggerToolbarHandler::OnUpdateUI)
777 EVT_UPDATE_UI(idMenuStepIntoInstr, DebuggerToolbarHandler::OnUpdateUI)
778 EVT_UPDATE_UI(idMenuStep, DebuggerToolbarHandler::OnUpdateUI)
779 EVT_UPDATE_UI(idMenuStepOut, DebuggerToolbarHandler::OnUpdateUI)
780 EVT_UPDATE_UI(idMenuBreak, DebuggerToolbarHandler::OnUpdateUI)
781 EVT_UPDATE_UI(idToolbarStop, DebuggerToolbarHandler::OnUpdateUI)
782
783 EVT_MENU(idDebuggerToolInfo, DebuggerToolbarHandler::OnToolInfo)
784 EVT_MENU(idDebuggerToolWindows, DebuggerToolbarHandler::OnDebugWindows)
785 EVT_MENU(idToolbarStop, DebuggerToolbarHandler::OnStop)
786 END_EVENT_TABLE()
787
788 DebuggerToolbarHandler::DebuggerToolbarHandler(DebuggerMenuHandler *menuHandler) :
789 m_Toolbar(nullptr),
790 m_menuHandler(menuHandler)
791 {
792 }
793
GetToolbar(bool create)794 wxToolBar* DebuggerToolbarHandler::GetToolbar(bool create)
795 {
796 if (!m_Toolbar)
797 {
798 if (!create)
799 return nullptr;
800
801 m_Toolbar = Manager::Get()->CreateEmptyToolbar();
802 Manager::AddonToolBar(m_Toolbar, wxT("debugger_toolbar"));
803
804 m_Toolbar->Realize();
805 m_Toolbar->SetInitialSize();
806 }
807 return m_Toolbar;
808 }
809
OnUpdateUI(wxUpdateUIEvent & event)810 void DebuggerToolbarHandler::OnUpdateUI(wxUpdateUIEvent& event)
811 {
812 cbDebuggerPlugin *plugin = Manager::Get()->GetDebuggerManager()->GetActiveDebugger();
813 ProjectManager *manager = Manager::Get()->GetProjectManager();
814
815 bool en = false;
816 bool stopped = false, isRunning = false;
817
818 if (plugin)
819 {
820 cbProject* prj = manager->GetActiveProject();
821 en = (prj && !prj->GetCurrentlyCompilingTarget()) || plugin->IsAttachedToProcess();
822 stopped = plugin->IsStopped();
823 isRunning = plugin->IsRunning();
824 }
825
826 if (m_Toolbar)
827 {
828 cbEditor* ed = Manager::Get()->GetEditorManager()->GetBuiltinActiveEditor();
829
830 cbPlugin *runningPlugin = manager->GetIsRunning();
831 if (runningPlugin != NULL && runningPlugin != plugin)
832 en = false;
833
834 m_Toolbar->EnableTool(idMenuDebug, (!isRunning || stopped) && en);
835 m_Toolbar->EnableTool(idMenuRunToCursor, en && ed && stopped);
836 m_Toolbar->EnableTool(idMenuNext, isRunning && en && stopped);
837 m_Toolbar->EnableTool(idMenuNextInstr, isRunning && en && stopped);
838 m_Toolbar->EnableTool(idMenuStepIntoInstr, isRunning && en && stopped);
839 m_Toolbar->EnableTool(idMenuStep, en && stopped);
840 m_Toolbar->EnableTool(idMenuStepOut, isRunning && en && stopped);
841 m_Toolbar->EnableTool(idToolbarStop, isRunning && en);
842 m_Toolbar->EnableTool(idMenuBreak, isRunning && !stopped && en);
843 m_Toolbar->EnableTool(idDebuggerToolInfo, plugin && plugin->ToolMenuEnabled());
844 }
845
846 // allow other UpdateUI handlers to process this event
847 // *very* important! don't forget it...
848 event.Skip();
849 }
850
OnToolInfo(cb_unused wxCommandEvent & event)851 void DebuggerToolbarHandler::OnToolInfo(cb_unused wxCommandEvent& event)
852 {
853 cbDebuggerPlugin *plugin = Manager::Get()->GetDebuggerManager()->GetActiveDebugger();
854 if (plugin)
855 {
856 wxMenu menu;
857 plugin->SetupToolsMenu(menu);
858 Manager::Get()->GetAppWindow()->PopupMenu(&menu);
859 }
860 }
861
OnDebugWindows(cb_unused wxCommandEvent & event)862 void DebuggerToolbarHandler::OnDebugWindows(cb_unused wxCommandEvent& event)
863 {
864 wxMenu m;
865 m_menuHandler->AppendWindowMenuItems(m);
866 Manager::Get()->GetAppWindow()->PopupMenu(&m);
867 }
868
OnStop(cb_unused wxCommandEvent & event)869 void DebuggerToolbarHandler::OnStop(cb_unused wxCommandEvent& event)
870 {
871 DebuggerManager *manager = Manager::Get()->GetDebuggerManager();
872 cbDebuggerPlugin *plugin = manager->GetActiveDebugger();
873 if (!plugin)
874 return;
875
876 if (plugin->IsAttachedToProcess())
877 {
878 wxMenu m;
879
880 if (plugin->IsStopped())
881 m.Append(idMenuDetach, _("Detach"));
882 else
883 {
884 wxMenuItem *detach_item = m.Append(idMenuDetach, _("Detach (debugger is running)"));
885 detach_item->Enable(false);
886 }
887
888 m.Append(idMenuStop, _("Stop debugger (kills the debuggee)"));
889
890 Manager::Get()->GetAppWindow()->PopupMenu(&m);
891 }
892 else
893 {
894 wxCommandEvent event2(wxEVT_COMMAND_TOOL_CLICKED, idMenuStop);
895 m_Toolbar->GetEventHandler()->ProcessEvent(event2);
896 }
897 }
898