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