15ffd83dbSDimitry Andric //===-- IOHandlerCursesGUI.cpp --------------------------------------------===//
2480093f4SDimitry Andric //
3480093f4SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4480093f4SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
5480093f4SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6480093f4SDimitry Andric //
7480093f4SDimitry Andric //===----------------------------------------------------------------------===//
8480093f4SDimitry Andric 
9480093f4SDimitry Andric #include "lldb/Core/IOHandlerCursesGUI.h"
10480093f4SDimitry Andric #include "lldb/Host/Config.h"
11480093f4SDimitry Andric 
12480093f4SDimitry Andric #if LLDB_ENABLE_CURSES
13e8d8bef9SDimitry Andric #if CURSES_HAVE_NCURSES_CURSES_H
14e8d8bef9SDimitry Andric #include <ncurses/curses.h>
15e8d8bef9SDimitry Andric #include <ncurses/panel.h>
16e8d8bef9SDimitry Andric #else
17480093f4SDimitry Andric #include <curses.h>
18480093f4SDimitry Andric #include <panel.h>
19480093f4SDimitry Andric #endif
20e8d8bef9SDimitry Andric #endif
21480093f4SDimitry Andric 
22480093f4SDimitry Andric #if defined(__APPLE__)
23480093f4SDimitry Andric #include <deque>
24480093f4SDimitry Andric #endif
25480093f4SDimitry Andric #include <string>
26480093f4SDimitry Andric 
27480093f4SDimitry Andric #include "lldb/Core/Debugger.h"
28fe6060f1SDimitry Andric #include "lldb/Core/ValueObjectUpdater.h"
29480093f4SDimitry Andric #include "lldb/Host/File.h"
3081ad6265SDimitry Andric #include "lldb/Utility/AnsiTerminal.h"
31480093f4SDimitry Andric #include "lldb/Utility/Predicate.h"
32480093f4SDimitry Andric #include "lldb/Utility/Status.h"
33480093f4SDimitry Andric #include "lldb/Utility/StreamString.h"
34480093f4SDimitry Andric #include "lldb/Utility/StringList.h"
35480093f4SDimitry Andric #include "lldb/lldb-forward.h"
36480093f4SDimitry Andric 
37480093f4SDimitry Andric #include "lldb/Interpreter/CommandCompletions.h"
38480093f4SDimitry Andric #include "lldb/Interpreter/CommandInterpreter.h"
39349cc55cSDimitry Andric #include "lldb/Interpreter/OptionGroupPlatform.h"
40480093f4SDimitry Andric 
41480093f4SDimitry Andric #if LLDB_ENABLE_CURSES
42480093f4SDimitry Andric #include "lldb/Breakpoint/BreakpointLocation.h"
43480093f4SDimitry Andric #include "lldb/Core/Module.h"
44fe6060f1SDimitry Andric #include "lldb/Core/PluginManager.h"
45480093f4SDimitry Andric #include "lldb/Core/ValueObject.h"
46480093f4SDimitry Andric #include "lldb/Core/ValueObjectRegister.h"
47480093f4SDimitry Andric #include "lldb/Symbol/Block.h"
48349cc55cSDimitry Andric #include "lldb/Symbol/CompileUnit.h"
49480093f4SDimitry Andric #include "lldb/Symbol/Function.h"
50480093f4SDimitry Andric #include "lldb/Symbol/Symbol.h"
51480093f4SDimitry Andric #include "lldb/Symbol/VariableList.h"
52480093f4SDimitry Andric #include "lldb/Target/Process.h"
53480093f4SDimitry Andric #include "lldb/Target/RegisterContext.h"
54480093f4SDimitry Andric #include "lldb/Target/StackFrame.h"
55480093f4SDimitry Andric #include "lldb/Target/StopInfo.h"
56480093f4SDimitry Andric #include "lldb/Target/Target.h"
57480093f4SDimitry Andric #include "lldb/Target/Thread.h"
58480093f4SDimitry Andric #include "lldb/Utility/State.h"
59480093f4SDimitry Andric #endif
60480093f4SDimitry Andric 
61480093f4SDimitry Andric #include "llvm/ADT/StringRef.h"
62480093f4SDimitry Andric 
63480093f4SDimitry Andric #ifdef _WIN32
64480093f4SDimitry Andric #include "lldb/Host/windows/windows.h"
65480093f4SDimitry Andric #endif
66480093f4SDimitry Andric 
67480093f4SDimitry Andric #include <memory>
68480093f4SDimitry Andric #include <mutex>
69480093f4SDimitry Andric 
70fe6060f1SDimitry Andric #include <cassert>
71fe6060f1SDimitry Andric #include <cctype>
72fe6060f1SDimitry Andric #include <cerrno>
73fe6060f1SDimitry Andric #include <cstdint>
74fe6060f1SDimitry Andric #include <cstdio>
75fe6060f1SDimitry Andric #include <cstring>
76fe6060f1SDimitry Andric #include <functional>
77bdd1243dSDimitry Andric #include <optional>
78480093f4SDimitry Andric #include <type_traits>
79480093f4SDimitry Andric 
80480093f4SDimitry Andric using namespace lldb;
81480093f4SDimitry Andric using namespace lldb_private;
82480093f4SDimitry Andric using llvm::StringRef;
83480093f4SDimitry Andric 
84480093f4SDimitry Andric // we may want curses to be disabled for some builds for instance, windows
85480093f4SDimitry Andric #if LLDB_ENABLE_CURSES
86480093f4SDimitry Andric 
87349cc55cSDimitry Andric #define KEY_CTRL_A 1
88349cc55cSDimitry Andric #define KEY_CTRL_E 5
89349cc55cSDimitry Andric #define KEY_CTRL_K 11
90480093f4SDimitry Andric #define KEY_RETURN 10
91480093f4SDimitry Andric #define KEY_ESCAPE 27
92349cc55cSDimitry Andric #define KEY_DELETE 127
93480093f4SDimitry Andric 
94fe6060f1SDimitry Andric #define KEY_SHIFT_TAB (KEY_MAX + 1)
95349cc55cSDimitry Andric #define KEY_ALT_ENTER (KEY_MAX + 2)
96fe6060f1SDimitry Andric 
97480093f4SDimitry Andric namespace curses {
98480093f4SDimitry Andric class Menu;
99480093f4SDimitry Andric class MenuDelegate;
100480093f4SDimitry Andric class Window;
101480093f4SDimitry Andric class WindowDelegate;
102480093f4SDimitry Andric typedef std::shared_ptr<Menu> MenuSP;
103480093f4SDimitry Andric typedef std::shared_ptr<MenuDelegate> MenuDelegateSP;
104480093f4SDimitry Andric typedef std::shared_ptr<Window> WindowSP;
105480093f4SDimitry Andric typedef std::shared_ptr<WindowDelegate> WindowDelegateSP;
106480093f4SDimitry Andric typedef std::vector<MenuSP> Menus;
107480093f4SDimitry Andric typedef std::vector<WindowSP> Windows;
108480093f4SDimitry Andric typedef std::vector<WindowDelegateSP> WindowDelegates;
109480093f4SDimitry Andric 
110480093f4SDimitry Andric #if 0
111480093f4SDimitry Andric type summary add -s "x=${var.x}, y=${var.y}" curses::Point
112480093f4SDimitry Andric type summary add -s "w=${var.width}, h=${var.height}" curses::Size
113480093f4SDimitry Andric type summary add -s "${var.origin%S} ${var.size%S}" curses::Rect
114480093f4SDimitry Andric #endif
115480093f4SDimitry Andric 
116480093f4SDimitry Andric struct Point {
117480093f4SDimitry Andric   int x;
118480093f4SDimitry Andric   int y;
119480093f4SDimitry Andric 
Pointcurses::Point120480093f4SDimitry Andric   Point(int _x = 0, int _y = 0) : x(_x), y(_y) {}
121480093f4SDimitry Andric 
Clearcurses::Point122480093f4SDimitry Andric   void Clear() {
123480093f4SDimitry Andric     x = 0;
124480093f4SDimitry Andric     y = 0;
125480093f4SDimitry Andric   }
126480093f4SDimitry Andric 
operator +=curses::Point127480093f4SDimitry Andric   Point &operator+=(const Point &rhs) {
128480093f4SDimitry Andric     x += rhs.x;
129480093f4SDimitry Andric     y += rhs.y;
130480093f4SDimitry Andric     return *this;
131480093f4SDimitry Andric   }
132480093f4SDimitry Andric 
Dumpcurses::Point133480093f4SDimitry Andric   void Dump() { printf("(x=%i, y=%i)\n", x, y); }
134480093f4SDimitry Andric };
135480093f4SDimitry Andric 
operator ==(const Point & lhs,const Point & rhs)136480093f4SDimitry Andric bool operator==(const Point &lhs, const Point &rhs) {
137480093f4SDimitry Andric   return lhs.x == rhs.x && lhs.y == rhs.y;
138480093f4SDimitry Andric }
139480093f4SDimitry Andric 
operator !=(const Point & lhs,const Point & rhs)140480093f4SDimitry Andric bool operator!=(const Point &lhs, const Point &rhs) {
141480093f4SDimitry Andric   return lhs.x != rhs.x || lhs.y != rhs.y;
142480093f4SDimitry Andric }
143480093f4SDimitry Andric 
144480093f4SDimitry Andric struct Size {
145480093f4SDimitry Andric   int width;
146480093f4SDimitry Andric   int height;
Sizecurses::Size147480093f4SDimitry Andric   Size(int w = 0, int h = 0) : width(w), height(h) {}
148480093f4SDimitry Andric 
Clearcurses::Size149480093f4SDimitry Andric   void Clear() {
150480093f4SDimitry Andric     width = 0;
151480093f4SDimitry Andric     height = 0;
152480093f4SDimitry Andric   }
153480093f4SDimitry Andric 
Dumpcurses::Size154480093f4SDimitry Andric   void Dump() { printf("(w=%i, h=%i)\n", width, height); }
155480093f4SDimitry Andric };
156480093f4SDimitry Andric 
operator ==(const Size & lhs,const Size & rhs)157480093f4SDimitry Andric bool operator==(const Size &lhs, const Size &rhs) {
158480093f4SDimitry Andric   return lhs.width == rhs.width && lhs.height == rhs.height;
159480093f4SDimitry Andric }
160480093f4SDimitry Andric 
operator !=(const Size & lhs,const Size & rhs)161480093f4SDimitry Andric bool operator!=(const Size &lhs, const Size &rhs) {
162480093f4SDimitry Andric   return lhs.width != rhs.width || lhs.height != rhs.height;
163480093f4SDimitry Andric }
164480093f4SDimitry Andric 
165480093f4SDimitry Andric struct Rect {
166480093f4SDimitry Andric   Point origin;
167480093f4SDimitry Andric   Size size;
168480093f4SDimitry Andric 
Rectcurses::Rect169480093f4SDimitry Andric   Rect() : origin(), size() {}
170480093f4SDimitry Andric 
Rectcurses::Rect171480093f4SDimitry Andric   Rect(const Point &p, const Size &s) : origin(p), size(s) {}
172480093f4SDimitry Andric 
Clearcurses::Rect173480093f4SDimitry Andric   void Clear() {
174480093f4SDimitry Andric     origin.Clear();
175480093f4SDimitry Andric     size.Clear();
176480093f4SDimitry Andric   }
177480093f4SDimitry Andric 
Dumpcurses::Rect178480093f4SDimitry Andric   void Dump() {
179480093f4SDimitry Andric     printf("(x=%i, y=%i), w=%i, h=%i)\n", origin.x, origin.y, size.width,
180480093f4SDimitry Andric            size.height);
181480093f4SDimitry Andric   }
182480093f4SDimitry Andric 
Insetcurses::Rect183480093f4SDimitry Andric   void Inset(int w, int h) {
184480093f4SDimitry Andric     if (size.width > w * 2)
185480093f4SDimitry Andric       size.width -= w * 2;
186480093f4SDimitry Andric     origin.x += w;
187480093f4SDimitry Andric 
188480093f4SDimitry Andric     if (size.height > h * 2)
189480093f4SDimitry Andric       size.height -= h * 2;
190480093f4SDimitry Andric     origin.y += h;
191480093f4SDimitry Andric   }
192480093f4SDimitry Andric 
193480093f4SDimitry Andric   // Return a status bar rectangle which is the last line of this rectangle.
194480093f4SDimitry Andric   // This rectangle will be modified to not include the status bar area.
MakeStatusBarcurses::Rect195480093f4SDimitry Andric   Rect MakeStatusBar() {
196480093f4SDimitry Andric     Rect status_bar;
197480093f4SDimitry Andric     if (size.height > 1) {
198480093f4SDimitry Andric       status_bar.origin.x = origin.x;
199480093f4SDimitry Andric       status_bar.origin.y = size.height;
200480093f4SDimitry Andric       status_bar.size.width = size.width;
201480093f4SDimitry Andric       status_bar.size.height = 1;
202480093f4SDimitry Andric       --size.height;
203480093f4SDimitry Andric     }
204480093f4SDimitry Andric     return status_bar;
205480093f4SDimitry Andric   }
206480093f4SDimitry Andric 
207480093f4SDimitry Andric   // Return a menubar rectangle which is the first line of this rectangle. This
208480093f4SDimitry Andric   // rectangle will be modified to not include the menubar area.
MakeMenuBarcurses::Rect209480093f4SDimitry Andric   Rect MakeMenuBar() {
210480093f4SDimitry Andric     Rect menubar;
211480093f4SDimitry Andric     if (size.height > 1) {
212480093f4SDimitry Andric       menubar.origin.x = origin.x;
213480093f4SDimitry Andric       menubar.origin.y = origin.y;
214480093f4SDimitry Andric       menubar.size.width = size.width;
215480093f4SDimitry Andric       menubar.size.height = 1;
216480093f4SDimitry Andric       ++origin.y;
217480093f4SDimitry Andric       --size.height;
218480093f4SDimitry Andric     }
219480093f4SDimitry Andric     return menubar;
220480093f4SDimitry Andric   }
221480093f4SDimitry Andric 
HorizontalSplitPercentagecurses::Rect222480093f4SDimitry Andric   void HorizontalSplitPercentage(float top_percentage, Rect &top,
223480093f4SDimitry Andric                                  Rect &bottom) const {
224480093f4SDimitry Andric     float top_height = top_percentage * size.height;
225480093f4SDimitry Andric     HorizontalSplit(top_height, top, bottom);
226480093f4SDimitry Andric   }
227480093f4SDimitry Andric 
HorizontalSplitcurses::Rect228480093f4SDimitry Andric   void HorizontalSplit(int top_height, Rect &top, Rect &bottom) const {
229480093f4SDimitry Andric     top = *this;
230480093f4SDimitry Andric     if (top_height < size.height) {
231480093f4SDimitry Andric       top.size.height = top_height;
232480093f4SDimitry Andric       bottom.origin.x = origin.x;
233480093f4SDimitry Andric       bottom.origin.y = origin.y + top.size.height;
234480093f4SDimitry Andric       bottom.size.width = size.width;
235480093f4SDimitry Andric       bottom.size.height = size.height - top.size.height;
236480093f4SDimitry Andric     } else {
237480093f4SDimitry Andric       bottom.Clear();
238480093f4SDimitry Andric     }
239480093f4SDimitry Andric   }
240480093f4SDimitry Andric 
VerticalSplitPercentagecurses::Rect241480093f4SDimitry Andric   void VerticalSplitPercentage(float left_percentage, Rect &left,
242480093f4SDimitry Andric                                Rect &right) const {
243480093f4SDimitry Andric     float left_width = left_percentage * size.width;
244480093f4SDimitry Andric     VerticalSplit(left_width, left, right);
245480093f4SDimitry Andric   }
246480093f4SDimitry Andric 
VerticalSplitcurses::Rect247480093f4SDimitry Andric   void VerticalSplit(int left_width, Rect &left, Rect &right) const {
248480093f4SDimitry Andric     left = *this;
249480093f4SDimitry Andric     if (left_width < size.width) {
250480093f4SDimitry Andric       left.size.width = left_width;
251480093f4SDimitry Andric       right.origin.x = origin.x + left.size.width;
252480093f4SDimitry Andric       right.origin.y = origin.y;
253480093f4SDimitry Andric       right.size.width = size.width - left.size.width;
254480093f4SDimitry Andric       right.size.height = size.height;
255480093f4SDimitry Andric     } else {
256480093f4SDimitry Andric       right.Clear();
257480093f4SDimitry Andric     }
258480093f4SDimitry Andric   }
259480093f4SDimitry Andric };
260480093f4SDimitry Andric 
operator ==(const Rect & lhs,const Rect & rhs)261480093f4SDimitry Andric bool operator==(const Rect &lhs, const Rect &rhs) {
262480093f4SDimitry Andric   return lhs.origin == rhs.origin && lhs.size == rhs.size;
263480093f4SDimitry Andric }
264480093f4SDimitry Andric 
operator !=(const Rect & lhs,const Rect & rhs)265480093f4SDimitry Andric bool operator!=(const Rect &lhs, const Rect &rhs) {
266480093f4SDimitry Andric   return lhs.origin != rhs.origin || lhs.size != rhs.size;
267480093f4SDimitry Andric }
268480093f4SDimitry Andric 
269480093f4SDimitry Andric enum HandleCharResult {
270480093f4SDimitry Andric   eKeyNotHandled = 0,
271480093f4SDimitry Andric   eKeyHandled = 1,
272480093f4SDimitry Andric   eQuitApplication = 2
273480093f4SDimitry Andric };
274480093f4SDimitry Andric 
275480093f4SDimitry Andric enum class MenuActionResult {
276480093f4SDimitry Andric   Handled,
277480093f4SDimitry Andric   NotHandled,
278480093f4SDimitry Andric   Quit // Exit all menus and quit
279480093f4SDimitry Andric };
280480093f4SDimitry Andric 
281480093f4SDimitry Andric struct KeyHelp {
282480093f4SDimitry Andric   int ch;
283480093f4SDimitry Andric   const char *description;
284480093f4SDimitry Andric };
285480093f4SDimitry Andric 
286e8d8bef9SDimitry Andric // COLOR_PAIR index names
287e8d8bef9SDimitry Andric enum {
288e8d8bef9SDimitry Andric   // First 16 colors are 8 black background and 8 blue background colors,
289e8d8bef9SDimitry Andric   // needed by OutputColoredStringTruncated().
290e8d8bef9SDimitry Andric   BlackOnBlack = 1,
291e8d8bef9SDimitry Andric   RedOnBlack,
292e8d8bef9SDimitry Andric   GreenOnBlack,
293e8d8bef9SDimitry Andric   YellowOnBlack,
294e8d8bef9SDimitry Andric   BlueOnBlack,
295e8d8bef9SDimitry Andric   MagentaOnBlack,
296e8d8bef9SDimitry Andric   CyanOnBlack,
297e8d8bef9SDimitry Andric   WhiteOnBlack,
298e8d8bef9SDimitry Andric   BlackOnBlue,
299e8d8bef9SDimitry Andric   RedOnBlue,
300e8d8bef9SDimitry Andric   GreenOnBlue,
301e8d8bef9SDimitry Andric   YellowOnBlue,
302e8d8bef9SDimitry Andric   BlueOnBlue,
303e8d8bef9SDimitry Andric   MagentaOnBlue,
304e8d8bef9SDimitry Andric   CyanOnBlue,
305e8d8bef9SDimitry Andric   WhiteOnBlue,
306e8d8bef9SDimitry Andric   // Other colors, as needed.
307e8d8bef9SDimitry Andric   BlackOnWhite,
308e8d8bef9SDimitry Andric   MagentaOnWhite,
309e8d8bef9SDimitry Andric   LastColorPairIndex = MagentaOnWhite
310e8d8bef9SDimitry Andric };
311e8d8bef9SDimitry Andric 
312480093f4SDimitry Andric class WindowDelegate {
313480093f4SDimitry Andric public:
314480093f4SDimitry Andric   virtual ~WindowDelegate() = default;
315480093f4SDimitry Andric 
WindowDelegateDraw(Window & window,bool force)316480093f4SDimitry Andric   virtual bool WindowDelegateDraw(Window &window, bool force) {
317480093f4SDimitry Andric     return false; // Drawing not handled
318480093f4SDimitry Andric   }
319480093f4SDimitry Andric 
WindowDelegateHandleChar(Window & window,int key)320480093f4SDimitry Andric   virtual HandleCharResult WindowDelegateHandleChar(Window &window, int key) {
321480093f4SDimitry Andric     return eKeyNotHandled;
322480093f4SDimitry Andric   }
323480093f4SDimitry Andric 
WindowDelegateGetHelpText()324480093f4SDimitry Andric   virtual const char *WindowDelegateGetHelpText() { return nullptr; }
325480093f4SDimitry Andric 
WindowDelegateGetKeyHelp()326480093f4SDimitry Andric   virtual KeyHelp *WindowDelegateGetKeyHelp() { return nullptr; }
327480093f4SDimitry Andric };
328480093f4SDimitry Andric 
329480093f4SDimitry Andric class HelpDialogDelegate : public WindowDelegate {
330480093f4SDimitry Andric public:
331480093f4SDimitry Andric   HelpDialogDelegate(const char *text, KeyHelp *key_help_array);
332480093f4SDimitry Andric 
333480093f4SDimitry Andric   ~HelpDialogDelegate() override;
334480093f4SDimitry Andric 
335480093f4SDimitry Andric   bool WindowDelegateDraw(Window &window, bool force) override;
336480093f4SDimitry Andric 
337480093f4SDimitry Andric   HandleCharResult WindowDelegateHandleChar(Window &window, int key) override;
338480093f4SDimitry Andric 
GetNumLines() const339480093f4SDimitry Andric   size_t GetNumLines() const { return m_text.GetSize(); }
340480093f4SDimitry Andric 
GetMaxLineLength() const341480093f4SDimitry Andric   size_t GetMaxLineLength() const { return m_text.GetMaxStringLength(); }
342480093f4SDimitry Andric 
343480093f4SDimitry Andric protected:
344480093f4SDimitry Andric   StringList m_text;
34581ad6265SDimitry Andric   int m_first_visible_line = 0;
346480093f4SDimitry Andric };
347480093f4SDimitry Andric 
348fe6060f1SDimitry Andric // A surface is an abstraction for something than can be drawn on. The surface
349fe6060f1SDimitry Andric // have a width, a height, a cursor position, and a multitude of drawing
350fe6060f1SDimitry Andric // operations. This type should be sub-classed to get an actually useful ncurses
351349cc55cSDimitry Andric // object, such as a Window or a Pad.
352fe6060f1SDimitry Andric class Surface {
353480093f4SDimitry Andric public:
354349cc55cSDimitry Andric   enum class Type { Window, Pad };
355349cc55cSDimitry Andric 
Surface(Surface::Type type)35681ad6265SDimitry Andric   Surface(Surface::Type type) : m_type(type) {}
357480093f4SDimitry Andric 
get()358fe6060f1SDimitry Andric   WINDOW *get() { return m_window; }
359fe6060f1SDimitry Andric 
operator WINDOW*()360fe6060f1SDimitry Andric   operator WINDOW *() { return m_window; }
361fe6060f1SDimitry Andric 
SubSurface(Rect bounds)362349cc55cSDimitry Andric   Surface SubSurface(Rect bounds) {
363349cc55cSDimitry Andric     Surface subSurface(m_type);
364349cc55cSDimitry Andric     if (m_type == Type::Pad)
365349cc55cSDimitry Andric       subSurface.m_window =
366349cc55cSDimitry Andric           ::subpad(m_window, bounds.size.height, bounds.size.width,
367349cc55cSDimitry Andric                    bounds.origin.y, bounds.origin.x);
368349cc55cSDimitry Andric     else
369349cc55cSDimitry Andric       subSurface.m_window =
370349cc55cSDimitry Andric           ::derwin(m_window, bounds.size.height, bounds.size.width,
371349cc55cSDimitry Andric                    bounds.origin.y, bounds.origin.x);
372349cc55cSDimitry Andric     return subSurface;
373349cc55cSDimitry Andric   }
374349cc55cSDimitry Andric 
375fe6060f1SDimitry Andric   // Copy a region of the surface to another surface.
CopyToSurface(Surface & target,Point source_origin,Point target_origin,Size size)376fe6060f1SDimitry Andric   void CopyToSurface(Surface &target, Point source_origin, Point target_origin,
377fe6060f1SDimitry Andric                      Size size) {
378fe6060f1SDimitry Andric     ::copywin(m_window, target.get(), source_origin.y, source_origin.x,
379fe6060f1SDimitry Andric               target_origin.y, target_origin.x,
380fe6060f1SDimitry Andric               target_origin.y + size.height - 1,
381fe6060f1SDimitry Andric               target_origin.x + size.width - 1, false);
382480093f4SDimitry Andric   }
383480093f4SDimitry Andric 
GetCursorX() const384fe6060f1SDimitry Andric   int GetCursorX() const { return getcurx(m_window); }
GetCursorY() const385fe6060f1SDimitry Andric   int GetCursorY() const { return getcury(m_window); }
MoveCursor(int x,int y)386fe6060f1SDimitry Andric   void MoveCursor(int x, int y) { ::wmove(m_window, y, x); }
387480093f4SDimitry Andric 
AttributeOn(attr_t attr)388480093f4SDimitry Andric   void AttributeOn(attr_t attr) { ::wattron(m_window, attr); }
AttributeOff(attr_t attr)389480093f4SDimitry Andric   void AttributeOff(attr_t attr) { ::wattroff(m_window, attr); }
390fe6060f1SDimitry Andric 
GetMaxX() const391e8d8bef9SDimitry Andric   int GetMaxX() const { return getmaxx(m_window); }
GetMaxY() const392e8d8bef9SDimitry Andric   int GetMaxY() const { return getmaxy(m_window); }
GetWidth() const393e8d8bef9SDimitry Andric   int GetWidth() const { return GetMaxX(); }
GetHeight() const394e8d8bef9SDimitry Andric   int GetHeight() const { return GetMaxY(); }
GetSize() const395fe6060f1SDimitry Andric   Size GetSize() const { return Size(GetWidth(), GetHeight()); }
396fe6060f1SDimitry Andric   // Get a zero origin rectangle width the surface size.
GetFrame() const397fe6060f1SDimitry Andric   Rect GetFrame() const { return Rect(Point(), GetSize()); }
398fe6060f1SDimitry Andric 
Clear()399fe6060f1SDimitry Andric   void Clear() { ::wclear(m_window); }
Erase()400fe6060f1SDimitry Andric   void Erase() { ::werase(m_window); }
401fe6060f1SDimitry Andric 
SetBackground(int color_pair_idx)402480093f4SDimitry Andric   void SetBackground(int color_pair_idx) {
403480093f4SDimitry Andric     ::wbkgd(m_window, COLOR_PAIR(color_pair_idx));
404480093f4SDimitry Andric   }
405480093f4SDimitry Andric 
PutChar(int ch)406fe6060f1SDimitry Andric   void PutChar(int ch) { ::waddch(m_window, ch); }
PutCString(const char * s,int len=-1)407fe6060f1SDimitry Andric   void PutCString(const char *s, int len = -1) { ::waddnstr(m_window, s, len); }
408fe6060f1SDimitry Andric 
PutCStringTruncated(int right_pad,const char * s,int len=-1)409e8d8bef9SDimitry Andric   void PutCStringTruncated(int right_pad, const char *s, int len = -1) {
410480093f4SDimitry Andric     int bytes_left = GetWidth() - GetCursorX();
411480093f4SDimitry Andric     if (bytes_left > right_pad) {
412480093f4SDimitry Andric       bytes_left -= right_pad;
413e8d8bef9SDimitry Andric       ::waddnstr(m_window, s, len < 0 ? bytes_left : std::min(bytes_left, len));
414480093f4SDimitry Andric     }
415480093f4SDimitry Andric   }
416480093f4SDimitry Andric 
Printf(const char * format,...)417480093f4SDimitry Andric   void Printf(const char *format, ...) __attribute__((format(printf, 2, 3))) {
418480093f4SDimitry Andric     va_list args;
419480093f4SDimitry Andric     va_start(args, format);
420fe6060f1SDimitry Andric     vw_printw(m_window, format, args);
421480093f4SDimitry Andric     va_end(args);
422480093f4SDimitry Andric   }
423480093f4SDimitry Andric 
PrintfTruncated(int right_pad,const char * format,...)424e8d8bef9SDimitry Andric   void PrintfTruncated(int right_pad, const char *format, ...)
425e8d8bef9SDimitry Andric       __attribute__((format(printf, 3, 4))) {
426e8d8bef9SDimitry Andric     va_list args;
427e8d8bef9SDimitry Andric     va_start(args, format);
428e8d8bef9SDimitry Andric     StreamString strm;
429e8d8bef9SDimitry Andric     strm.PrintfVarArg(format, args);
430e8d8bef9SDimitry Andric     va_end(args);
431e8d8bef9SDimitry Andric     PutCStringTruncated(right_pad, strm.GetData());
432e8d8bef9SDimitry Andric   }
433e8d8bef9SDimitry Andric 
VerticalLine(int n,chtype v_char=ACS_VLINE)434fe6060f1SDimitry Andric   void VerticalLine(int n, chtype v_char = ACS_VLINE) {
435fe6060f1SDimitry Andric     ::wvline(m_window, v_char, n);
436fe6060f1SDimitry Andric   }
HorizontalLine(int n,chtype h_char=ACS_HLINE)437fe6060f1SDimitry Andric   void HorizontalLine(int n, chtype h_char = ACS_HLINE) {
438fe6060f1SDimitry Andric     ::whline(m_window, h_char, n);
439fe6060f1SDimitry Andric   }
Box(chtype v_char=ACS_VLINE,chtype h_char=ACS_HLINE)440fe6060f1SDimitry Andric   void Box(chtype v_char = ACS_VLINE, chtype h_char = ACS_HLINE) {
441fe6060f1SDimitry Andric     ::box(m_window, v_char, h_char);
442fe6060f1SDimitry Andric   }
443fe6060f1SDimitry Andric 
TitledBox(const char * title,chtype v_char=ACS_VLINE,chtype h_char=ACS_HLINE)444fe6060f1SDimitry Andric   void TitledBox(const char *title, chtype v_char = ACS_VLINE,
445fe6060f1SDimitry Andric                  chtype h_char = ACS_HLINE) {
446fe6060f1SDimitry Andric     Box(v_char, h_char);
447fe6060f1SDimitry Andric     int title_offset = 2;
448fe6060f1SDimitry Andric     MoveCursor(title_offset, 0);
449fe6060f1SDimitry Andric     PutChar('[');
450fe6060f1SDimitry Andric     PutCString(title, GetWidth() - title_offset);
451fe6060f1SDimitry Andric     PutChar(']');
452fe6060f1SDimitry Andric   }
453fe6060f1SDimitry Andric 
Box(const Rect & bounds,chtype v_char=ACS_VLINE,chtype h_char=ACS_HLINE)454fe6060f1SDimitry Andric   void Box(const Rect &bounds, chtype v_char = ACS_VLINE,
455fe6060f1SDimitry Andric            chtype h_char = ACS_HLINE) {
456fe6060f1SDimitry Andric     MoveCursor(bounds.origin.x, bounds.origin.y);
457fe6060f1SDimitry Andric     VerticalLine(bounds.size.height);
458fe6060f1SDimitry Andric     HorizontalLine(bounds.size.width);
459fe6060f1SDimitry Andric     PutChar(ACS_ULCORNER);
460fe6060f1SDimitry Andric 
461fe6060f1SDimitry Andric     MoveCursor(bounds.origin.x + bounds.size.width - 1, bounds.origin.y);
462fe6060f1SDimitry Andric     VerticalLine(bounds.size.height);
463fe6060f1SDimitry Andric     PutChar(ACS_URCORNER);
464fe6060f1SDimitry Andric 
465fe6060f1SDimitry Andric     MoveCursor(bounds.origin.x, bounds.origin.y + bounds.size.height - 1);
466fe6060f1SDimitry Andric     HorizontalLine(bounds.size.width);
467fe6060f1SDimitry Andric     PutChar(ACS_LLCORNER);
468fe6060f1SDimitry Andric 
469fe6060f1SDimitry Andric     MoveCursor(bounds.origin.x + bounds.size.width - 1,
470fe6060f1SDimitry Andric                bounds.origin.y + bounds.size.height - 1);
471fe6060f1SDimitry Andric     PutChar(ACS_LRCORNER);
472fe6060f1SDimitry Andric   }
473fe6060f1SDimitry Andric 
TitledBox(const Rect & bounds,const char * title,chtype v_char=ACS_VLINE,chtype h_char=ACS_HLINE)474fe6060f1SDimitry Andric   void TitledBox(const Rect &bounds, const char *title,
475fe6060f1SDimitry Andric                  chtype v_char = ACS_VLINE, chtype h_char = ACS_HLINE) {
476fe6060f1SDimitry Andric     Box(bounds, v_char, h_char);
477fe6060f1SDimitry Andric     int title_offset = 2;
478fe6060f1SDimitry Andric     MoveCursor(bounds.origin.x + title_offset, bounds.origin.y);
479fe6060f1SDimitry Andric     PutChar('[');
480fe6060f1SDimitry Andric     PutCString(title, bounds.size.width - title_offset);
481fe6060f1SDimitry Andric     PutChar(']');
482e8d8bef9SDimitry Andric   }
483e8d8bef9SDimitry Andric 
484e8d8bef9SDimitry Andric   // Curses doesn't allow direct output of color escape sequences, but that's
485e8d8bef9SDimitry Andric   // how we get source lines from the Highligher class. Read the line and
486e8d8bef9SDimitry Andric   // convert color escape sequences to curses color attributes. Use
487e8d8bef9SDimitry Andric   // first_skip_count to skip leading visible characters. Returns false if all
488e8d8bef9SDimitry Andric   // visible characters were skipped due to first_skip_count.
OutputColoredStringTruncated(int right_pad,StringRef string,size_t skip_first_count,bool use_blue_background)489e8d8bef9SDimitry Andric   bool OutputColoredStringTruncated(int right_pad, StringRef string,
490e8d8bef9SDimitry Andric                                     size_t skip_first_count,
491e8d8bef9SDimitry Andric                                     bool use_blue_background) {
492e8d8bef9SDimitry Andric     attr_t saved_attr;
493e8d8bef9SDimitry Andric     short saved_pair;
494e8d8bef9SDimitry Andric     bool result = false;
495e8d8bef9SDimitry Andric     wattr_get(m_window, &saved_attr, &saved_pair, nullptr);
496e8d8bef9SDimitry Andric     if (use_blue_background)
497e8d8bef9SDimitry Andric       ::wattron(m_window, COLOR_PAIR(WhiteOnBlue));
498e8d8bef9SDimitry Andric     while (!string.empty()) {
49981ad6265SDimitry Andric       size_t esc_pos = string.find(ANSI_ESC_START);
500e8d8bef9SDimitry Andric       if (esc_pos == StringRef::npos) {
501e8d8bef9SDimitry Andric         string = string.substr(skip_first_count);
502e8d8bef9SDimitry Andric         if (!string.empty()) {
503e8d8bef9SDimitry Andric           PutCStringTruncated(right_pad, string.data(), string.size());
504e8d8bef9SDimitry Andric           result = true;
505e8d8bef9SDimitry Andric         }
506e8d8bef9SDimitry Andric         break;
507e8d8bef9SDimitry Andric       }
508e8d8bef9SDimitry Andric       if (esc_pos > 0) {
509e8d8bef9SDimitry Andric         if (skip_first_count > 0) {
510e8d8bef9SDimitry Andric           int skip = std::min(esc_pos, skip_first_count);
511e8d8bef9SDimitry Andric           string = string.substr(skip);
512e8d8bef9SDimitry Andric           skip_first_count -= skip;
513e8d8bef9SDimitry Andric           esc_pos -= skip;
514e8d8bef9SDimitry Andric         }
515e8d8bef9SDimitry Andric         if (esc_pos > 0) {
516e8d8bef9SDimitry Andric           PutCStringTruncated(right_pad, string.data(), esc_pos);
517e8d8bef9SDimitry Andric           result = true;
518e8d8bef9SDimitry Andric           string = string.drop_front(esc_pos);
519e8d8bef9SDimitry Andric         }
520e8d8bef9SDimitry Andric       }
52181ad6265SDimitry Andric       bool consumed = string.consume_front(ANSI_ESC_START);
522e8d8bef9SDimitry Andric       assert(consumed);
523e8d8bef9SDimitry Andric       UNUSED_IF_ASSERT_DISABLED(consumed);
524e8d8bef9SDimitry Andric       // This is written to match our Highlighter classes, which seem to
525e8d8bef9SDimitry Andric       // generate only foreground color escape sequences. If necessary, this
526e8d8bef9SDimitry Andric       // will need to be extended.
52781ad6265SDimitry Andric       // Only 8 basic foreground colors, underline and reset, our Highlighter
52881ad6265SDimitry Andric       // doesn't use anything else.
529e8d8bef9SDimitry Andric       int value;
530e8d8bef9SDimitry Andric       if (!!string.consumeInteger(10, value) || // Returns false on success.
53181ad6265SDimitry Andric           !(value == 0 || value == ANSI_CTRL_UNDERLINE ||
53281ad6265SDimitry Andric             (value >= ANSI_FG_COLOR_BLACK && value <= ANSI_FG_COLOR_WHITE))) {
533e8d8bef9SDimitry Andric         llvm::errs() << "No valid color code in color escape sequence.\n";
534e8d8bef9SDimitry Andric         continue;
535e8d8bef9SDimitry Andric       }
53681ad6265SDimitry Andric       if (!string.consume_front(ANSI_ESC_END)) {
53781ad6265SDimitry Andric         llvm::errs() << "Missing '" << ANSI_ESC_END
53881ad6265SDimitry Andric                      << "' in color escape sequence.\n";
539e8d8bef9SDimitry Andric         continue;
540e8d8bef9SDimitry Andric       }
541e8d8bef9SDimitry Andric       if (value == 0) { // Reset.
542e8d8bef9SDimitry Andric         wattr_set(m_window, saved_attr, saved_pair, nullptr);
543e8d8bef9SDimitry Andric         if (use_blue_background)
544e8d8bef9SDimitry Andric           ::wattron(m_window, COLOR_PAIR(WhiteOnBlue));
54581ad6265SDimitry Andric       } else if (value == ANSI_CTRL_UNDERLINE) {
54681ad6265SDimitry Andric         ::wattron(m_window, A_UNDERLINE);
547e8d8bef9SDimitry Andric       } else {
548e8d8bef9SDimitry Andric         // Mapped directly to first 16 color pairs (black/blue background).
54981ad6265SDimitry Andric         ::wattron(m_window, COLOR_PAIR(value - ANSI_FG_COLOR_BLACK + 1 +
55081ad6265SDimitry Andric                                        (use_blue_background ? 8 : 0)));
551e8d8bef9SDimitry Andric       }
552e8d8bef9SDimitry Andric     }
553e8d8bef9SDimitry Andric     wattr_set(m_window, saved_attr, saved_pair, nullptr);
554e8d8bef9SDimitry Andric     return result;
555e8d8bef9SDimitry Andric   }
556e8d8bef9SDimitry Andric 
557fe6060f1SDimitry Andric protected:
558349cc55cSDimitry Andric   Type m_type;
55981ad6265SDimitry Andric   WINDOW *m_window = nullptr;
560fe6060f1SDimitry Andric };
561fe6060f1SDimitry Andric 
562fe6060f1SDimitry Andric class Pad : public Surface {
563fe6060f1SDimitry Andric public:
Pad(Size size)564349cc55cSDimitry Andric   Pad(Size size) : Surface(Surface::Type::Pad) {
565349cc55cSDimitry Andric     m_window = ::newpad(size.height, size.width);
566349cc55cSDimitry Andric   }
567fe6060f1SDimitry Andric 
~Pad()568fe6060f1SDimitry Andric   ~Pad() { ::delwin(m_window); }
569fe6060f1SDimitry Andric };
570fe6060f1SDimitry Andric 
571fe6060f1SDimitry Andric class Window : public Surface {
572fe6060f1SDimitry Andric public:
Window(const char * name)573fe6060f1SDimitry Andric   Window(const char *name)
574349cc55cSDimitry Andric       : Surface(Surface::Type::Window), m_name(name), m_panel(nullptr),
575349cc55cSDimitry Andric         m_parent(nullptr), m_subwindows(), m_delegate_sp(),
576349cc55cSDimitry Andric         m_curr_active_window_idx(UINT32_MAX),
577fe6060f1SDimitry Andric         m_prev_active_window_idx(UINT32_MAX), m_delete(false),
578fe6060f1SDimitry Andric         m_needs_update(true), m_can_activate(true), m_is_subwin(false) {}
579fe6060f1SDimitry Andric 
Window(const char * name,WINDOW * w,bool del=true)580fe6060f1SDimitry Andric   Window(const char *name, WINDOW *w, bool del = true)
581349cc55cSDimitry Andric       : Surface(Surface::Type::Window), m_name(name), m_panel(nullptr),
582349cc55cSDimitry Andric         m_parent(nullptr), m_subwindows(), m_delegate_sp(),
583349cc55cSDimitry Andric         m_curr_active_window_idx(UINT32_MAX),
584fe6060f1SDimitry Andric         m_prev_active_window_idx(UINT32_MAX), m_delete(del),
585fe6060f1SDimitry Andric         m_needs_update(true), m_can_activate(true), m_is_subwin(false) {
586fe6060f1SDimitry Andric     if (w)
587fe6060f1SDimitry Andric       Reset(w);
588fe6060f1SDimitry Andric   }
589fe6060f1SDimitry Andric 
Window(const char * name,const Rect & bounds)590fe6060f1SDimitry Andric   Window(const char *name, const Rect &bounds)
591bdd1243dSDimitry Andric       : Surface(Surface::Type::Window), m_name(name), m_panel(nullptr),
592bdd1243dSDimitry Andric         m_parent(nullptr), m_subwindows(), m_delegate_sp(),
593bdd1243dSDimitry Andric         m_curr_active_window_idx(UINT32_MAX),
594bdd1243dSDimitry Andric         m_prev_active_window_idx(UINT32_MAX), m_delete(false),
595fe6060f1SDimitry Andric         m_needs_update(true), m_can_activate(true), m_is_subwin(false) {
596fe6060f1SDimitry Andric     Reset(::newwin(bounds.size.height, bounds.size.width, bounds.origin.y,
597fe6060f1SDimitry Andric                    bounds.origin.y));
598fe6060f1SDimitry Andric   }
599fe6060f1SDimitry Andric 
~Window()600fe6060f1SDimitry Andric   virtual ~Window() {
601fe6060f1SDimitry Andric     RemoveSubWindows();
602fe6060f1SDimitry Andric     Reset();
603fe6060f1SDimitry Andric   }
604fe6060f1SDimitry Andric 
Reset(WINDOW * w=nullptr,bool del=true)605fe6060f1SDimitry Andric   void Reset(WINDOW *w = nullptr, bool del = true) {
606fe6060f1SDimitry Andric     if (m_window == w)
607fe6060f1SDimitry Andric       return;
608fe6060f1SDimitry Andric 
609fe6060f1SDimitry Andric     if (m_panel) {
610fe6060f1SDimitry Andric       ::del_panel(m_panel);
611fe6060f1SDimitry Andric       m_panel = nullptr;
612fe6060f1SDimitry Andric     }
613fe6060f1SDimitry Andric     if (m_window && m_delete) {
614fe6060f1SDimitry Andric       ::delwin(m_window);
615fe6060f1SDimitry Andric       m_window = nullptr;
616fe6060f1SDimitry Andric       m_delete = false;
617fe6060f1SDimitry Andric     }
618fe6060f1SDimitry Andric     if (w) {
619fe6060f1SDimitry Andric       m_window = w;
620fe6060f1SDimitry Andric       m_panel = ::new_panel(m_window);
621fe6060f1SDimitry Andric       m_delete = del;
622fe6060f1SDimitry Andric     }
623fe6060f1SDimitry Andric   }
624fe6060f1SDimitry Andric 
625fe6060f1SDimitry Andric   // Get the rectangle in our parent window
GetBounds() const626fe6060f1SDimitry Andric   Rect GetBounds() const { return Rect(GetParentOrigin(), GetSize()); }
627fe6060f1SDimitry Andric 
GetCenteredRect(int width,int height)628fe6060f1SDimitry Andric   Rect GetCenteredRect(int width, int height) {
629fe6060f1SDimitry Andric     Size size = GetSize();
630fe6060f1SDimitry Andric     width = std::min(size.width, width);
631fe6060f1SDimitry Andric     height = std::min(size.height, height);
632fe6060f1SDimitry Andric     int x = (size.width - width) / 2;
633fe6060f1SDimitry Andric     int y = (size.height - height) / 2;
634fe6060f1SDimitry Andric     return Rect(Point(x, y), Size(width, height));
635fe6060f1SDimitry Andric   }
636fe6060f1SDimitry Andric 
GetChar()637fe6060f1SDimitry Andric   int GetChar() { return ::wgetch(m_window); }
GetParentOrigin() const638fe6060f1SDimitry Andric   Point GetParentOrigin() const { return Point(GetParentX(), GetParentY()); }
GetParentX() const639fe6060f1SDimitry Andric   int GetParentX() const { return getparx(m_window); }
GetParentY() const640fe6060f1SDimitry Andric   int GetParentY() const { return getpary(m_window); }
MoveWindow(int x,int y)641fe6060f1SDimitry Andric   void MoveWindow(int x, int y) { MoveWindow(Point(x, y)); }
Resize(int w,int h)642fe6060f1SDimitry Andric   void Resize(int w, int h) { ::wresize(m_window, h, w); }
Resize(const Size & size)643fe6060f1SDimitry Andric   void Resize(const Size &size) {
644fe6060f1SDimitry Andric     ::wresize(m_window, size.height, size.width);
645fe6060f1SDimitry Andric   }
MoveWindow(const Point & origin)646fe6060f1SDimitry Andric   void MoveWindow(const Point &origin) {
647fe6060f1SDimitry Andric     const bool moving_window = origin != GetParentOrigin();
648fe6060f1SDimitry Andric     if (m_is_subwin && moving_window) {
649fe6060f1SDimitry Andric       // Can't move subwindows, must delete and re-create
650fe6060f1SDimitry Andric       Size size = GetSize();
651fe6060f1SDimitry Andric       Reset(::subwin(m_parent->m_window, size.height, size.width, origin.y,
652fe6060f1SDimitry Andric                      origin.x),
653fe6060f1SDimitry Andric             true);
654fe6060f1SDimitry Andric     } else {
655fe6060f1SDimitry Andric       ::mvwin(m_window, origin.y, origin.x);
656fe6060f1SDimitry Andric     }
657fe6060f1SDimitry Andric   }
658fe6060f1SDimitry Andric 
SetBounds(const Rect & bounds)659fe6060f1SDimitry Andric   void SetBounds(const Rect &bounds) {
660fe6060f1SDimitry Andric     const bool moving_window = bounds.origin != GetParentOrigin();
661fe6060f1SDimitry Andric     if (m_is_subwin && moving_window) {
662fe6060f1SDimitry Andric       // Can't move subwindows, must delete and re-create
663fe6060f1SDimitry Andric       Reset(::subwin(m_parent->m_window, bounds.size.height, bounds.size.width,
664fe6060f1SDimitry Andric                      bounds.origin.y, bounds.origin.x),
665fe6060f1SDimitry Andric             true);
666fe6060f1SDimitry Andric     } else {
667fe6060f1SDimitry Andric       if (moving_window)
668fe6060f1SDimitry Andric         MoveWindow(bounds.origin);
669fe6060f1SDimitry Andric       Resize(bounds.size);
670fe6060f1SDimitry Andric     }
671fe6060f1SDimitry Andric   }
672fe6060f1SDimitry Andric 
Touch()673480093f4SDimitry Andric   void Touch() {
674480093f4SDimitry Andric     ::touchwin(m_window);
675480093f4SDimitry Andric     if (m_parent)
676480093f4SDimitry Andric       m_parent->Touch();
677480093f4SDimitry Andric   }
678480093f4SDimitry Andric 
CreateSubWindow(const char * name,const Rect & bounds,bool make_active)679480093f4SDimitry Andric   WindowSP CreateSubWindow(const char *name, const Rect &bounds,
680480093f4SDimitry Andric                            bool make_active) {
681480093f4SDimitry Andric     auto get_window = [this, &bounds]() {
682480093f4SDimitry Andric       return m_window
683480093f4SDimitry Andric                  ? ::subwin(m_window, bounds.size.height, bounds.size.width,
684480093f4SDimitry Andric                             bounds.origin.y, bounds.origin.x)
685480093f4SDimitry Andric                  : ::newwin(bounds.size.height, bounds.size.width,
686480093f4SDimitry Andric                             bounds.origin.y, bounds.origin.x);
687480093f4SDimitry Andric     };
688480093f4SDimitry Andric     WindowSP subwindow_sp = std::make_shared<Window>(name, get_window(), true);
689480093f4SDimitry Andric     subwindow_sp->m_is_subwin = subwindow_sp.operator bool();
690480093f4SDimitry Andric     subwindow_sp->m_parent = this;
691480093f4SDimitry Andric     if (make_active) {
692480093f4SDimitry Andric       m_prev_active_window_idx = m_curr_active_window_idx;
693480093f4SDimitry Andric       m_curr_active_window_idx = m_subwindows.size();
694480093f4SDimitry Andric     }
695480093f4SDimitry Andric     m_subwindows.push_back(subwindow_sp);
696480093f4SDimitry Andric     ::top_panel(subwindow_sp->m_panel);
697480093f4SDimitry Andric     m_needs_update = true;
698480093f4SDimitry Andric     return subwindow_sp;
699480093f4SDimitry Andric   }
700480093f4SDimitry Andric 
RemoveSubWindow(Window * window)701480093f4SDimitry Andric   bool RemoveSubWindow(Window *window) {
702480093f4SDimitry Andric     Windows::iterator pos, end = m_subwindows.end();
703480093f4SDimitry Andric     size_t i = 0;
704480093f4SDimitry Andric     for (pos = m_subwindows.begin(); pos != end; ++pos, ++i) {
705480093f4SDimitry Andric       if ((*pos).get() == window) {
706480093f4SDimitry Andric         if (m_prev_active_window_idx == i)
707480093f4SDimitry Andric           m_prev_active_window_idx = UINT32_MAX;
708480093f4SDimitry Andric         else if (m_prev_active_window_idx != UINT32_MAX &&
709480093f4SDimitry Andric                  m_prev_active_window_idx > i)
710480093f4SDimitry Andric           --m_prev_active_window_idx;
711480093f4SDimitry Andric 
712480093f4SDimitry Andric         if (m_curr_active_window_idx == i)
713480093f4SDimitry Andric           m_curr_active_window_idx = UINT32_MAX;
714480093f4SDimitry Andric         else if (m_curr_active_window_idx != UINT32_MAX &&
715480093f4SDimitry Andric                  m_curr_active_window_idx > i)
716480093f4SDimitry Andric           --m_curr_active_window_idx;
717480093f4SDimitry Andric         window->Erase();
718480093f4SDimitry Andric         m_subwindows.erase(pos);
719480093f4SDimitry Andric         m_needs_update = true;
720480093f4SDimitry Andric         if (m_parent)
721480093f4SDimitry Andric           m_parent->Touch();
722480093f4SDimitry Andric         else
723480093f4SDimitry Andric           ::touchwin(stdscr);
724480093f4SDimitry Andric         return true;
725480093f4SDimitry Andric       }
726480093f4SDimitry Andric     }
727480093f4SDimitry Andric     return false;
728480093f4SDimitry Andric   }
729480093f4SDimitry Andric 
FindSubWindow(const char * name)730480093f4SDimitry Andric   WindowSP FindSubWindow(const char *name) {
731480093f4SDimitry Andric     Windows::iterator pos, end = m_subwindows.end();
732480093f4SDimitry Andric     size_t i = 0;
733480093f4SDimitry Andric     for (pos = m_subwindows.begin(); pos != end; ++pos, ++i) {
734480093f4SDimitry Andric       if ((*pos)->m_name == name)
735480093f4SDimitry Andric         return *pos;
736480093f4SDimitry Andric     }
737480093f4SDimitry Andric     return WindowSP();
738480093f4SDimitry Andric   }
739480093f4SDimitry Andric 
RemoveSubWindows()740480093f4SDimitry Andric   void RemoveSubWindows() {
741480093f4SDimitry Andric     m_curr_active_window_idx = UINT32_MAX;
742480093f4SDimitry Andric     m_prev_active_window_idx = UINT32_MAX;
743480093f4SDimitry Andric     for (Windows::iterator pos = m_subwindows.begin();
744480093f4SDimitry Andric          pos != m_subwindows.end(); pos = m_subwindows.erase(pos)) {
745480093f4SDimitry Andric       (*pos)->Erase();
746480093f4SDimitry Andric     }
747480093f4SDimitry Andric     if (m_parent)
748480093f4SDimitry Andric       m_parent->Touch();
749480093f4SDimitry Andric     else
750480093f4SDimitry Andric       ::touchwin(stdscr);
751480093f4SDimitry Andric   }
752480093f4SDimitry Andric 
753480093f4SDimitry Andric   // Window drawing utilities
DrawTitleBox(const char * title,const char * bottom_message=nullptr)754480093f4SDimitry Andric   void DrawTitleBox(const char *title, const char *bottom_message = nullptr) {
755480093f4SDimitry Andric     attr_t attr = 0;
756480093f4SDimitry Andric     if (IsActive())
757e8d8bef9SDimitry Andric       attr = A_BOLD | COLOR_PAIR(BlackOnWhite);
758480093f4SDimitry Andric     else
759480093f4SDimitry Andric       attr = 0;
760480093f4SDimitry Andric     if (attr)
761480093f4SDimitry Andric       AttributeOn(attr);
762480093f4SDimitry Andric 
763480093f4SDimitry Andric     Box();
764480093f4SDimitry Andric     MoveCursor(3, 0);
765480093f4SDimitry Andric 
766480093f4SDimitry Andric     if (title && title[0]) {
767480093f4SDimitry Andric       PutChar('<');
768480093f4SDimitry Andric       PutCString(title);
769480093f4SDimitry Andric       PutChar('>');
770480093f4SDimitry Andric     }
771480093f4SDimitry Andric 
772480093f4SDimitry Andric     if (bottom_message && bottom_message[0]) {
773480093f4SDimitry Andric       int bottom_message_length = strlen(bottom_message);
774480093f4SDimitry Andric       int x = GetWidth() - 3 - (bottom_message_length + 2);
775480093f4SDimitry Andric 
776480093f4SDimitry Andric       if (x > 0) {
777480093f4SDimitry Andric         MoveCursor(x, GetHeight() - 1);
778480093f4SDimitry Andric         PutChar('[');
779480093f4SDimitry Andric         PutCString(bottom_message);
780480093f4SDimitry Andric         PutChar(']');
781480093f4SDimitry Andric       } else {
782480093f4SDimitry Andric         MoveCursor(1, GetHeight() - 1);
783480093f4SDimitry Andric         PutChar('[');
784e8d8bef9SDimitry Andric         PutCStringTruncated(1, bottom_message);
785480093f4SDimitry Andric       }
786480093f4SDimitry Andric     }
787480093f4SDimitry Andric     if (attr)
788480093f4SDimitry Andric       AttributeOff(attr);
789480093f4SDimitry Andric   }
790480093f4SDimitry Andric 
Draw(bool force)791480093f4SDimitry Andric   virtual void Draw(bool force) {
792480093f4SDimitry Andric     if (m_delegate_sp && m_delegate_sp->WindowDelegateDraw(*this, force))
793480093f4SDimitry Andric       return;
794480093f4SDimitry Andric 
795480093f4SDimitry Andric     for (auto &subwindow_sp : m_subwindows)
796480093f4SDimitry Andric       subwindow_sp->Draw(force);
797480093f4SDimitry Andric   }
798480093f4SDimitry Andric 
CreateHelpSubwindow()799480093f4SDimitry Andric   bool CreateHelpSubwindow() {
800480093f4SDimitry Andric     if (m_delegate_sp) {
801480093f4SDimitry Andric       const char *text = m_delegate_sp->WindowDelegateGetHelpText();
802480093f4SDimitry Andric       KeyHelp *key_help = m_delegate_sp->WindowDelegateGetKeyHelp();
803480093f4SDimitry Andric       if ((text && text[0]) || key_help) {
804480093f4SDimitry Andric         std::unique_ptr<HelpDialogDelegate> help_delegate_up(
805480093f4SDimitry Andric             new HelpDialogDelegate(text, key_help));
806480093f4SDimitry Andric         const size_t num_lines = help_delegate_up->GetNumLines();
807480093f4SDimitry Andric         const size_t max_length = help_delegate_up->GetMaxLineLength();
808480093f4SDimitry Andric         Rect bounds = GetBounds();
809480093f4SDimitry Andric         bounds.Inset(1, 1);
810480093f4SDimitry Andric         if (max_length + 4 < static_cast<size_t>(bounds.size.width)) {
811480093f4SDimitry Andric           bounds.origin.x += (bounds.size.width - max_length + 4) / 2;
812480093f4SDimitry Andric           bounds.size.width = max_length + 4;
813480093f4SDimitry Andric         } else {
814480093f4SDimitry Andric           if (bounds.size.width > 100) {
815480093f4SDimitry Andric             const int inset_w = bounds.size.width / 4;
816480093f4SDimitry Andric             bounds.origin.x += inset_w;
817480093f4SDimitry Andric             bounds.size.width -= 2 * inset_w;
818480093f4SDimitry Andric           }
819480093f4SDimitry Andric         }
820480093f4SDimitry Andric 
821480093f4SDimitry Andric         if (num_lines + 2 < static_cast<size_t>(bounds.size.height)) {
822480093f4SDimitry Andric           bounds.origin.y += (bounds.size.height - num_lines + 2) / 2;
823480093f4SDimitry Andric           bounds.size.height = num_lines + 2;
824480093f4SDimitry Andric         } else {
825480093f4SDimitry Andric           if (bounds.size.height > 100) {
826480093f4SDimitry Andric             const int inset_h = bounds.size.height / 4;
827480093f4SDimitry Andric             bounds.origin.y += inset_h;
828480093f4SDimitry Andric             bounds.size.height -= 2 * inset_h;
829480093f4SDimitry Andric           }
830480093f4SDimitry Andric         }
831480093f4SDimitry Andric         WindowSP help_window_sp;
832480093f4SDimitry Andric         Window *parent_window = GetParent();
833480093f4SDimitry Andric         if (parent_window)
834480093f4SDimitry Andric           help_window_sp = parent_window->CreateSubWindow("Help", bounds, true);
835480093f4SDimitry Andric         else
836480093f4SDimitry Andric           help_window_sp = CreateSubWindow("Help", bounds, true);
837480093f4SDimitry Andric         help_window_sp->SetDelegate(
838480093f4SDimitry Andric             WindowDelegateSP(help_delegate_up.release()));
839480093f4SDimitry Andric         return true;
840480093f4SDimitry Andric       }
841480093f4SDimitry Andric     }
842480093f4SDimitry Andric     return false;
843480093f4SDimitry Andric   }
844480093f4SDimitry Andric 
HandleChar(int key)845480093f4SDimitry Andric   virtual HandleCharResult HandleChar(int key) {
846480093f4SDimitry Andric     // Always check the active window first
847480093f4SDimitry Andric     HandleCharResult result = eKeyNotHandled;
848480093f4SDimitry Andric     WindowSP active_window_sp = GetActiveWindow();
849480093f4SDimitry Andric     if (active_window_sp) {
850480093f4SDimitry Andric       result = active_window_sp->HandleChar(key);
851480093f4SDimitry Andric       if (result != eKeyNotHandled)
852480093f4SDimitry Andric         return result;
853480093f4SDimitry Andric     }
854480093f4SDimitry Andric 
855480093f4SDimitry Andric     if (m_delegate_sp) {
856480093f4SDimitry Andric       result = m_delegate_sp->WindowDelegateHandleChar(*this, key);
857480093f4SDimitry Andric       if (result != eKeyNotHandled)
858480093f4SDimitry Andric         return result;
859480093f4SDimitry Andric     }
860480093f4SDimitry Andric 
861480093f4SDimitry Andric     // Then check for any windows that want any keys that weren't handled. This
862480093f4SDimitry Andric     // is typically only for a menubar. Make a copy of the subwindows in case
863480093f4SDimitry Andric     // any HandleChar() functions muck with the subwindows. If we don't do
864480093f4SDimitry Andric     // this, we can crash when iterating over the subwindows.
865480093f4SDimitry Andric     Windows subwindows(m_subwindows);
866480093f4SDimitry Andric     for (auto subwindow_sp : subwindows) {
867480093f4SDimitry Andric       if (!subwindow_sp->m_can_activate) {
868480093f4SDimitry Andric         HandleCharResult result = subwindow_sp->HandleChar(key);
869480093f4SDimitry Andric         if (result != eKeyNotHandled)
870480093f4SDimitry Andric           return result;
871480093f4SDimitry Andric       }
872480093f4SDimitry Andric     }
873480093f4SDimitry Andric 
874480093f4SDimitry Andric     return eKeyNotHandled;
875480093f4SDimitry Andric   }
876480093f4SDimitry Andric 
GetActiveWindow()877480093f4SDimitry Andric   WindowSP GetActiveWindow() {
878480093f4SDimitry Andric     if (!m_subwindows.empty()) {
879480093f4SDimitry Andric       if (m_curr_active_window_idx >= m_subwindows.size()) {
880480093f4SDimitry Andric         if (m_prev_active_window_idx < m_subwindows.size()) {
881480093f4SDimitry Andric           m_curr_active_window_idx = m_prev_active_window_idx;
882480093f4SDimitry Andric           m_prev_active_window_idx = UINT32_MAX;
883480093f4SDimitry Andric         } else if (IsActive()) {
884480093f4SDimitry Andric           m_prev_active_window_idx = UINT32_MAX;
885480093f4SDimitry Andric           m_curr_active_window_idx = UINT32_MAX;
886480093f4SDimitry Andric 
887480093f4SDimitry Andric           // Find first window that wants to be active if this window is active
888480093f4SDimitry Andric           const size_t num_subwindows = m_subwindows.size();
889480093f4SDimitry Andric           for (size_t i = 0; i < num_subwindows; ++i) {
890480093f4SDimitry Andric             if (m_subwindows[i]->GetCanBeActive()) {
891480093f4SDimitry Andric               m_curr_active_window_idx = i;
892480093f4SDimitry Andric               break;
893480093f4SDimitry Andric             }
894480093f4SDimitry Andric           }
895480093f4SDimitry Andric         }
896480093f4SDimitry Andric       }
897480093f4SDimitry Andric 
898480093f4SDimitry Andric       if (m_curr_active_window_idx < m_subwindows.size())
899480093f4SDimitry Andric         return m_subwindows[m_curr_active_window_idx];
900480093f4SDimitry Andric     }
901480093f4SDimitry Andric     return WindowSP();
902480093f4SDimitry Andric   }
903480093f4SDimitry Andric 
GetCanBeActive() const904480093f4SDimitry Andric   bool GetCanBeActive() const { return m_can_activate; }
905480093f4SDimitry Andric 
SetCanBeActive(bool b)906480093f4SDimitry Andric   void SetCanBeActive(bool b) { m_can_activate = b; }
907480093f4SDimitry Andric 
SetDelegate(const WindowDelegateSP & delegate_sp)908480093f4SDimitry Andric   void SetDelegate(const WindowDelegateSP &delegate_sp) {
909480093f4SDimitry Andric     m_delegate_sp = delegate_sp;
910480093f4SDimitry Andric   }
911480093f4SDimitry Andric 
GetParent() const912480093f4SDimitry Andric   Window *GetParent() const { return m_parent; }
913480093f4SDimitry Andric 
IsActive() const914480093f4SDimitry Andric   bool IsActive() const {
915480093f4SDimitry Andric     if (m_parent)
916480093f4SDimitry Andric       return m_parent->GetActiveWindow().get() == this;
917480093f4SDimitry Andric     else
918480093f4SDimitry Andric       return true; // Top level window is always active
919480093f4SDimitry Andric   }
920480093f4SDimitry Andric 
SelectNextWindowAsActive()921480093f4SDimitry Andric   void SelectNextWindowAsActive() {
922480093f4SDimitry Andric     // Move active focus to next window
923e8d8bef9SDimitry Andric     const int num_subwindows = m_subwindows.size();
924e8d8bef9SDimitry Andric     int start_idx = 0;
925e8d8bef9SDimitry Andric     if (m_curr_active_window_idx != UINT32_MAX) {
926480093f4SDimitry Andric       m_prev_active_window_idx = m_curr_active_window_idx;
927e8d8bef9SDimitry Andric       start_idx = m_curr_active_window_idx + 1;
928e8d8bef9SDimitry Andric     }
929e8d8bef9SDimitry Andric     for (int idx = start_idx; idx < num_subwindows; ++idx) {
930480093f4SDimitry Andric       if (m_subwindows[idx]->GetCanBeActive()) {
931480093f4SDimitry Andric         m_curr_active_window_idx = idx;
932e8d8bef9SDimitry Andric         return;
933480093f4SDimitry Andric       }
934480093f4SDimitry Andric     }
935e8d8bef9SDimitry Andric     for (int idx = 0; idx < start_idx; ++idx) {
936480093f4SDimitry Andric       if (m_subwindows[idx]->GetCanBeActive()) {
937480093f4SDimitry Andric         m_curr_active_window_idx = idx;
938480093f4SDimitry Andric         break;
939480093f4SDimitry Andric       }
940480093f4SDimitry Andric     }
941480093f4SDimitry Andric   }
942e8d8bef9SDimitry Andric 
SelectPreviousWindowAsActive()943e8d8bef9SDimitry Andric   void SelectPreviousWindowAsActive() {
944e8d8bef9SDimitry Andric     // Move active focus to previous window
945e8d8bef9SDimitry Andric     const int num_subwindows = m_subwindows.size();
946e8d8bef9SDimitry Andric     int start_idx = num_subwindows - 1;
947e8d8bef9SDimitry Andric     if (m_curr_active_window_idx != UINT32_MAX) {
948480093f4SDimitry Andric       m_prev_active_window_idx = m_curr_active_window_idx;
949e8d8bef9SDimitry Andric       start_idx = m_curr_active_window_idx - 1;
950e8d8bef9SDimitry Andric     }
951e8d8bef9SDimitry Andric     for (int idx = start_idx; idx >= 0; --idx) {
952e8d8bef9SDimitry Andric       if (m_subwindows[idx]->GetCanBeActive()) {
953e8d8bef9SDimitry Andric         m_curr_active_window_idx = idx;
954e8d8bef9SDimitry Andric         return;
955e8d8bef9SDimitry Andric       }
956e8d8bef9SDimitry Andric     }
957e8d8bef9SDimitry Andric     for (int idx = num_subwindows - 1; idx > start_idx; --idx) {
958480093f4SDimitry Andric       if (m_subwindows[idx]->GetCanBeActive()) {
959480093f4SDimitry Andric         m_curr_active_window_idx = idx;
960480093f4SDimitry Andric         break;
961480093f4SDimitry Andric       }
962480093f4SDimitry Andric     }
963480093f4SDimitry Andric   }
964480093f4SDimitry Andric 
GetName() const965480093f4SDimitry Andric   const char *GetName() const { return m_name.c_str(); }
966480093f4SDimitry Andric 
967480093f4SDimitry Andric protected:
968480093f4SDimitry Andric   std::string m_name;
969480093f4SDimitry Andric   PANEL *m_panel;
970480093f4SDimitry Andric   Window *m_parent;
971480093f4SDimitry Andric   Windows m_subwindows;
972480093f4SDimitry Andric   WindowDelegateSP m_delegate_sp;
973480093f4SDimitry Andric   uint32_t m_curr_active_window_idx;
974480093f4SDimitry Andric   uint32_t m_prev_active_window_idx;
975480093f4SDimitry Andric   bool m_delete;
976480093f4SDimitry Andric   bool m_needs_update;
977480093f4SDimitry Andric   bool m_can_activate;
978480093f4SDimitry Andric   bool m_is_subwin;
979480093f4SDimitry Andric 
980480093f4SDimitry Andric private:
9815ffd83dbSDimitry Andric   Window(const Window &) = delete;
9825ffd83dbSDimitry Andric   const Window &operator=(const Window &) = delete;
983480093f4SDimitry Andric };
984480093f4SDimitry Andric 
985fe6060f1SDimitry Andric /////////
986fe6060f1SDimitry Andric // Forms
987fe6060f1SDimitry Andric /////////
988fe6060f1SDimitry Andric 
989fe6060f1SDimitry Andric // A scroll context defines a vertical region that needs to be visible in a
990fe6060f1SDimitry Andric // scrolling area. The region is defined by the index of the start and end lines
991fe6060f1SDimitry Andric // of the region. The start and end lines may be equal, in which case, the
992fe6060f1SDimitry Andric // region is a single line.
993fe6060f1SDimitry Andric struct ScrollContext {
994fe6060f1SDimitry Andric   int start;
995fe6060f1SDimitry Andric   int end;
996fe6060f1SDimitry Andric 
ScrollContextcurses::ScrollContext997fe6060f1SDimitry Andric   ScrollContext(int line) : start(line), end(line) {}
ScrollContextcurses::ScrollContext998fe6060f1SDimitry Andric   ScrollContext(int _start, int _end) : start(_start), end(_end) {}
999fe6060f1SDimitry Andric 
Offsetcurses::ScrollContext1000fe6060f1SDimitry Andric   void Offset(int offset) {
1001fe6060f1SDimitry Andric     start += offset;
1002fe6060f1SDimitry Andric     end += offset;
1003fe6060f1SDimitry Andric   }
1004fe6060f1SDimitry Andric };
1005fe6060f1SDimitry Andric 
1006fe6060f1SDimitry Andric class FieldDelegate {
1007fe6060f1SDimitry Andric public:
1008fe6060f1SDimitry Andric   virtual ~FieldDelegate() = default;
1009fe6060f1SDimitry Andric 
1010fe6060f1SDimitry Andric   // Returns the number of lines needed to draw the field. The draw method will
1011fe6060f1SDimitry Andric   // be given a surface that have exactly this number of lines.
1012fe6060f1SDimitry Andric   virtual int FieldDelegateGetHeight() = 0;
1013fe6060f1SDimitry Andric 
1014fe6060f1SDimitry Andric   // Returns the scroll context in the local coordinates of the field. By
1015fe6060f1SDimitry Andric   // default, the scroll context spans the whole field. Bigger fields with
1016fe6060f1SDimitry Andric   // internal navigation should override this method to provide a finer context.
1017fe6060f1SDimitry Andric   // Typical override methods would first get the scroll context of the internal
1018fe6060f1SDimitry Andric   // element then add the offset of the element in the field.
FieldDelegateGetScrollContext()1019fe6060f1SDimitry Andric   virtual ScrollContext FieldDelegateGetScrollContext() {
1020fe6060f1SDimitry Andric     return ScrollContext(0, FieldDelegateGetHeight() - 1);
1021fe6060f1SDimitry Andric   }
1022fe6060f1SDimitry Andric 
1023fe6060f1SDimitry Andric   // Draw the field in the given subpad surface. The surface have a height that
1024fe6060f1SDimitry Andric   // is equal to the height returned by FieldDelegateGetHeight(). If the field
1025fe6060f1SDimitry Andric   // is selected in the form window, then is_selected will be true.
1026349cc55cSDimitry Andric   virtual void FieldDelegateDraw(Surface &surface, bool is_selected) = 0;
1027fe6060f1SDimitry Andric 
1028fe6060f1SDimitry Andric   // Handle the key that wasn't handled by the form window or a container field.
FieldDelegateHandleChar(int key)1029fe6060f1SDimitry Andric   virtual HandleCharResult FieldDelegateHandleChar(int key) {
1030fe6060f1SDimitry Andric     return eKeyNotHandled;
1031fe6060f1SDimitry Andric   }
1032fe6060f1SDimitry Andric 
1033fe6060f1SDimitry Andric   // This is executed once the user exists the field, that is, once the user
1034fe6060f1SDimitry Andric   // navigates to the next or the previous field. This is particularly useful to
1035fe6060f1SDimitry Andric   // do in-field validation and error setting. Fields with internal navigation
1036fe6060f1SDimitry Andric   // should call this method on their fields.
FieldDelegateExitCallback()10370eae32dcSDimitry Andric   virtual void FieldDelegateExitCallback() {}
1038fe6060f1SDimitry Andric 
1039fe6060f1SDimitry Andric   // Fields may have internal navigation, for instance, a List Field have
1040fe6060f1SDimitry Andric   // multiple internal elements, which needs to be navigated. To allow for this
1041fe6060f1SDimitry Andric   // mechanism, the window shouldn't handle the navigation keys all the time,
1042fe6060f1SDimitry Andric   // and instead call the key handing method of the selected field. It should
1043fe6060f1SDimitry Andric   // only handle the navigation keys when the field contains a single element or
1044fe6060f1SDimitry Andric   // have the last or first element selected depending on if the user is
1045fe6060f1SDimitry Andric   // navigating forward or backward. Additionally, once a field is selected in
1046fe6060f1SDimitry Andric   // the forward or backward direction, its first or last internal element
1047fe6060f1SDimitry Andric   // should be selected. The following methods implements those mechanisms.
1048fe6060f1SDimitry Andric 
1049fe6060f1SDimitry Andric   // Returns true if the first element in the field is selected or if the field
1050fe6060f1SDimitry Andric   // contains a single element.
FieldDelegateOnFirstOrOnlyElement()1051fe6060f1SDimitry Andric   virtual bool FieldDelegateOnFirstOrOnlyElement() { return true; }
1052fe6060f1SDimitry Andric 
1053fe6060f1SDimitry Andric   // Returns true if the last element in the field is selected or if the field
1054fe6060f1SDimitry Andric   // contains a single element.
FieldDelegateOnLastOrOnlyElement()1055fe6060f1SDimitry Andric   virtual bool FieldDelegateOnLastOrOnlyElement() { return true; }
1056fe6060f1SDimitry Andric 
1057fe6060f1SDimitry Andric   // Select the first element in the field if multiple elements exists.
FieldDelegateSelectFirstElement()10580eae32dcSDimitry Andric   virtual void FieldDelegateSelectFirstElement() {}
1059fe6060f1SDimitry Andric 
1060fe6060f1SDimitry Andric   // Select the last element in the field if multiple elements exists.
FieldDelegateSelectLastElement()10610eae32dcSDimitry Andric   virtual void FieldDelegateSelectLastElement() {}
1062fe6060f1SDimitry Andric 
1063fe6060f1SDimitry Andric   // Returns true if the field has an error, false otherwise.
FieldDelegateHasError()1064fe6060f1SDimitry Andric   virtual bool FieldDelegateHasError() { return false; }
1065fe6060f1SDimitry Andric 
FieldDelegateIsVisible()1066fe6060f1SDimitry Andric   bool FieldDelegateIsVisible() { return m_is_visible; }
1067fe6060f1SDimitry Andric 
FieldDelegateHide()1068fe6060f1SDimitry Andric   void FieldDelegateHide() { m_is_visible = false; }
1069fe6060f1SDimitry Andric 
FieldDelegateShow()1070fe6060f1SDimitry Andric   void FieldDelegateShow() { m_is_visible = true; }
1071fe6060f1SDimitry Andric 
1072fe6060f1SDimitry Andric protected:
1073fe6060f1SDimitry Andric   bool m_is_visible = true;
1074fe6060f1SDimitry Andric };
1075fe6060f1SDimitry Andric 
1076fe6060f1SDimitry Andric typedef std::unique_ptr<FieldDelegate> FieldDelegateUP;
1077fe6060f1SDimitry Andric 
1078fe6060f1SDimitry Andric class TextFieldDelegate : public FieldDelegate {
1079fe6060f1SDimitry Andric public:
TextFieldDelegate(const char * label,const char * content,bool required)1080fe6060f1SDimitry Andric   TextFieldDelegate(const char *label, const char *content, bool required)
108181ad6265SDimitry Andric       : m_label(label), m_required(required) {
1082fe6060f1SDimitry Andric     if (content)
1083fe6060f1SDimitry Andric       m_content = content;
1084fe6060f1SDimitry Andric   }
1085fe6060f1SDimitry Andric 
1086fe6060f1SDimitry Andric   // Text fields are drawn as titled boxes of a single line, with a possible
1087fe6060f1SDimitry Andric   // error messages at the end.
1088fe6060f1SDimitry Andric   //
1089fe6060f1SDimitry Andric   // __[Label]___________
1090fe6060f1SDimitry Andric   // |                  |
1091fe6060f1SDimitry Andric   // |__________________|
1092fe6060f1SDimitry Andric   // - Error message if it exists.
1093fe6060f1SDimitry Andric 
1094fe6060f1SDimitry Andric   // The text field has a height of 3 lines. 2 lines for borders and 1 line for
1095fe6060f1SDimitry Andric   // the content.
GetFieldHeight()1096fe6060f1SDimitry Andric   int GetFieldHeight() { return 3; }
1097fe6060f1SDimitry Andric 
1098fe6060f1SDimitry Andric   // The text field has a full height of 3 or 4 lines. 3 lines for the actual
1099fe6060f1SDimitry Andric   // field and an optional line for an error if it exists.
FieldDelegateGetHeight()1100fe6060f1SDimitry Andric   int FieldDelegateGetHeight() override {
1101fe6060f1SDimitry Andric     int height = GetFieldHeight();
1102fe6060f1SDimitry Andric     if (FieldDelegateHasError())
1103fe6060f1SDimitry Andric       height++;
1104fe6060f1SDimitry Andric     return height;
1105fe6060f1SDimitry Andric   }
1106fe6060f1SDimitry Andric 
1107fe6060f1SDimitry Andric   // Get the cursor X position in the surface coordinate.
GetCursorXPosition()1108fe6060f1SDimitry Andric   int GetCursorXPosition() { return m_cursor_position - m_first_visibile_char; }
1109fe6060f1SDimitry Andric 
GetContentLength()1110fe6060f1SDimitry Andric   int GetContentLength() { return m_content.length(); }
1111fe6060f1SDimitry Andric 
DrawContent(Surface & surface,bool is_selected)1112349cc55cSDimitry Andric   void DrawContent(Surface &surface, bool is_selected) {
1113349cc55cSDimitry Andric     UpdateScrolling(surface.GetWidth());
1114349cc55cSDimitry Andric 
1115fe6060f1SDimitry Andric     surface.MoveCursor(0, 0);
1116fe6060f1SDimitry Andric     const char *text = m_content.c_str() + m_first_visibile_char;
1117fe6060f1SDimitry Andric     surface.PutCString(text, surface.GetWidth());
1118fe6060f1SDimitry Andric 
1119fe6060f1SDimitry Andric     // Highlight the cursor.
1120fe6060f1SDimitry Andric     surface.MoveCursor(GetCursorXPosition(), 0);
1121fe6060f1SDimitry Andric     if (is_selected)
1122fe6060f1SDimitry Andric       surface.AttributeOn(A_REVERSE);
1123fe6060f1SDimitry Andric     if (m_cursor_position == GetContentLength())
1124fe6060f1SDimitry Andric       // Cursor is past the last character. Highlight an empty space.
1125fe6060f1SDimitry Andric       surface.PutChar(' ');
1126fe6060f1SDimitry Andric     else
1127fe6060f1SDimitry Andric       surface.PutChar(m_content[m_cursor_position]);
1128fe6060f1SDimitry Andric     if (is_selected)
1129fe6060f1SDimitry Andric       surface.AttributeOff(A_REVERSE);
1130fe6060f1SDimitry Andric   }
1131fe6060f1SDimitry Andric 
DrawField(Surface & surface,bool is_selected)1132349cc55cSDimitry Andric   void DrawField(Surface &surface, bool is_selected) {
1133fe6060f1SDimitry Andric     surface.TitledBox(m_label.c_str());
1134fe6060f1SDimitry Andric 
1135fe6060f1SDimitry Andric     Rect content_bounds = surface.GetFrame();
1136fe6060f1SDimitry Andric     content_bounds.Inset(1, 1);
1137349cc55cSDimitry Andric     Surface content_surface = surface.SubSurface(content_bounds);
1138fe6060f1SDimitry Andric 
1139fe6060f1SDimitry Andric     DrawContent(content_surface, is_selected);
1140fe6060f1SDimitry Andric   }
1141fe6060f1SDimitry Andric 
DrawError(Surface & surface)1142349cc55cSDimitry Andric   void DrawError(Surface &surface) {
1143fe6060f1SDimitry Andric     if (!FieldDelegateHasError())
1144fe6060f1SDimitry Andric       return;
1145fe6060f1SDimitry Andric     surface.MoveCursor(0, 0);
1146fe6060f1SDimitry Andric     surface.AttributeOn(COLOR_PAIR(RedOnBlack));
1147fe6060f1SDimitry Andric     surface.PutChar(ACS_DIAMOND);
1148fe6060f1SDimitry Andric     surface.PutChar(' ');
1149fe6060f1SDimitry Andric     surface.PutCStringTruncated(1, GetError().c_str());
1150fe6060f1SDimitry Andric     surface.AttributeOff(COLOR_PAIR(RedOnBlack));
1151fe6060f1SDimitry Andric   }
1152fe6060f1SDimitry Andric 
FieldDelegateDraw(Surface & surface,bool is_selected)1153349cc55cSDimitry Andric   void FieldDelegateDraw(Surface &surface, bool is_selected) override {
1154fe6060f1SDimitry Andric     Rect frame = surface.GetFrame();
1155fe6060f1SDimitry Andric     Rect field_bounds, error_bounds;
1156fe6060f1SDimitry Andric     frame.HorizontalSplit(GetFieldHeight(), field_bounds, error_bounds);
1157349cc55cSDimitry Andric     Surface field_surface = surface.SubSurface(field_bounds);
1158349cc55cSDimitry Andric     Surface error_surface = surface.SubSurface(error_bounds);
1159fe6060f1SDimitry Andric 
1160fe6060f1SDimitry Andric     DrawField(field_surface, is_selected);
1161fe6060f1SDimitry Andric     DrawError(error_surface);
1162fe6060f1SDimitry Andric   }
1163fe6060f1SDimitry Andric 
1164349cc55cSDimitry Andric   // Get the position of the last visible character.
GetLastVisibleCharPosition(int width)1165349cc55cSDimitry Andric   int GetLastVisibleCharPosition(int width) {
1166349cc55cSDimitry Andric     int position = m_first_visibile_char + width - 1;
1167349cc55cSDimitry Andric     return std::min(position, GetContentLength());
1168349cc55cSDimitry Andric   }
1169349cc55cSDimitry Andric 
UpdateScrolling(int width)1170349cc55cSDimitry Andric   void UpdateScrolling(int width) {
1171349cc55cSDimitry Andric     if (m_cursor_position < m_first_visibile_char) {
1172349cc55cSDimitry Andric       m_first_visibile_char = m_cursor_position;
1173349cc55cSDimitry Andric       return;
1174349cc55cSDimitry Andric     }
1175349cc55cSDimitry Andric 
1176349cc55cSDimitry Andric     if (m_cursor_position > GetLastVisibleCharPosition(width))
1177349cc55cSDimitry Andric       m_first_visibile_char = m_cursor_position - (width - 1);
1178349cc55cSDimitry Andric   }
1179349cc55cSDimitry Andric 
1180fe6060f1SDimitry Andric   // The cursor is allowed to move one character past the string.
1181fe6060f1SDimitry Andric   // m_cursor_position is in range [0, GetContentLength()].
MoveCursorRight()1182fe6060f1SDimitry Andric   void MoveCursorRight() {
1183fe6060f1SDimitry Andric     if (m_cursor_position < GetContentLength())
1184fe6060f1SDimitry Andric       m_cursor_position++;
1185fe6060f1SDimitry Andric   }
1186fe6060f1SDimitry Andric 
MoveCursorLeft()1187fe6060f1SDimitry Andric   void MoveCursorLeft() {
1188fe6060f1SDimitry Andric     if (m_cursor_position > 0)
1189fe6060f1SDimitry Andric       m_cursor_position--;
1190fe6060f1SDimitry Andric   }
1191fe6060f1SDimitry Andric 
MoveCursorToStart()1192349cc55cSDimitry Andric   void MoveCursorToStart() { m_cursor_position = 0; }
1193349cc55cSDimitry Andric 
MoveCursorToEnd()1194349cc55cSDimitry Andric   void MoveCursorToEnd() { m_cursor_position = GetContentLength(); }
1195fe6060f1SDimitry Andric 
ScrollLeft()1196fe6060f1SDimitry Andric   void ScrollLeft() {
1197fe6060f1SDimitry Andric     if (m_first_visibile_char > 0)
1198fe6060f1SDimitry Andric       m_first_visibile_char--;
1199fe6060f1SDimitry Andric   }
1200fe6060f1SDimitry Andric 
1201349cc55cSDimitry Andric   // Insert a character at the current cursor position and advance the cursor
1202349cc55cSDimitry Andric   // position.
InsertChar(char character)1203fe6060f1SDimitry Andric   void InsertChar(char character) {
1204fe6060f1SDimitry Andric     m_content.insert(m_cursor_position, 1, character);
1205fe6060f1SDimitry Andric     m_cursor_position++;
1206349cc55cSDimitry Andric     ClearError();
1207fe6060f1SDimitry Andric   }
1208fe6060f1SDimitry Andric 
1209fe6060f1SDimitry Andric   // Remove the character before the cursor position, retreat the cursor
1210349cc55cSDimitry Andric   // position, and scroll left.
RemovePreviousChar()1211349cc55cSDimitry Andric   void RemovePreviousChar() {
1212fe6060f1SDimitry Andric     if (m_cursor_position == 0)
1213fe6060f1SDimitry Andric       return;
1214fe6060f1SDimitry Andric 
1215fe6060f1SDimitry Andric     m_content.erase(m_cursor_position - 1, 1);
1216fe6060f1SDimitry Andric     m_cursor_position--;
1217fe6060f1SDimitry Andric     ScrollLeft();
1218349cc55cSDimitry Andric     ClearError();
1219349cc55cSDimitry Andric   }
1220349cc55cSDimitry Andric 
1221349cc55cSDimitry Andric   // Remove the character after the cursor position.
RemoveNextChar()1222349cc55cSDimitry Andric   void RemoveNextChar() {
1223349cc55cSDimitry Andric     if (m_cursor_position == GetContentLength())
1224349cc55cSDimitry Andric       return;
1225349cc55cSDimitry Andric 
1226349cc55cSDimitry Andric     m_content.erase(m_cursor_position, 1);
1227349cc55cSDimitry Andric     ClearError();
1228349cc55cSDimitry Andric   }
1229349cc55cSDimitry Andric 
1230349cc55cSDimitry Andric   // Clear characters from the current cursor position to the end.
ClearToEnd()1231349cc55cSDimitry Andric   void ClearToEnd() {
1232349cc55cSDimitry Andric     m_content.erase(m_cursor_position);
1233349cc55cSDimitry Andric     ClearError();
1234349cc55cSDimitry Andric   }
1235349cc55cSDimitry Andric 
Clear()1236349cc55cSDimitry Andric   void Clear() {
1237349cc55cSDimitry Andric     m_content.clear();
1238349cc55cSDimitry Andric     m_cursor_position = 0;
1239349cc55cSDimitry Andric     ClearError();
1240fe6060f1SDimitry Andric   }
1241fe6060f1SDimitry Andric 
1242fe6060f1SDimitry Andric   // True if the key represents a char that can be inserted in the field
1243fe6060f1SDimitry Andric   // content, false otherwise.
IsAcceptableChar(int key)1244349cc55cSDimitry Andric   virtual bool IsAcceptableChar(int key) {
1245349cc55cSDimitry Andric     // The behavior of isprint is undefined when the value is not representable
1246349cc55cSDimitry Andric     // as an unsigned char. So explicitly check for non-ascii key codes.
1247349cc55cSDimitry Andric     if (key > 127)
1248349cc55cSDimitry Andric       return false;
1249349cc55cSDimitry Andric     return isprint(key);
1250349cc55cSDimitry Andric   }
1251fe6060f1SDimitry Andric 
FieldDelegateHandleChar(int key)1252fe6060f1SDimitry Andric   HandleCharResult FieldDelegateHandleChar(int key) override {
1253fe6060f1SDimitry Andric     if (IsAcceptableChar(key)) {
1254fe6060f1SDimitry Andric       ClearError();
1255fe6060f1SDimitry Andric       InsertChar((char)key);
1256fe6060f1SDimitry Andric       return eKeyHandled;
1257fe6060f1SDimitry Andric     }
1258fe6060f1SDimitry Andric 
1259fe6060f1SDimitry Andric     switch (key) {
1260349cc55cSDimitry Andric     case KEY_HOME:
1261349cc55cSDimitry Andric     case KEY_CTRL_A:
1262349cc55cSDimitry Andric       MoveCursorToStart();
1263349cc55cSDimitry Andric       return eKeyHandled;
1264349cc55cSDimitry Andric     case KEY_END:
1265349cc55cSDimitry Andric     case KEY_CTRL_E:
1266349cc55cSDimitry Andric       MoveCursorToEnd();
1267349cc55cSDimitry Andric       return eKeyHandled;
1268fe6060f1SDimitry Andric     case KEY_RIGHT:
1269349cc55cSDimitry Andric     case KEY_SF:
1270fe6060f1SDimitry Andric       MoveCursorRight();
1271fe6060f1SDimitry Andric       return eKeyHandled;
1272fe6060f1SDimitry Andric     case KEY_LEFT:
1273349cc55cSDimitry Andric     case KEY_SR:
1274fe6060f1SDimitry Andric       MoveCursorLeft();
1275fe6060f1SDimitry Andric       return eKeyHandled;
1276fe6060f1SDimitry Andric     case KEY_BACKSPACE:
1277349cc55cSDimitry Andric     case KEY_DELETE:
1278349cc55cSDimitry Andric       RemovePreviousChar();
1279349cc55cSDimitry Andric       return eKeyHandled;
1280349cc55cSDimitry Andric     case KEY_DC:
1281349cc55cSDimitry Andric       RemoveNextChar();
1282349cc55cSDimitry Andric       return eKeyHandled;
1283349cc55cSDimitry Andric     case KEY_EOL:
1284349cc55cSDimitry Andric     case KEY_CTRL_K:
1285349cc55cSDimitry Andric       ClearToEnd();
1286349cc55cSDimitry Andric       return eKeyHandled;
1287349cc55cSDimitry Andric     case KEY_DL:
1288349cc55cSDimitry Andric     case KEY_CLEAR:
1289349cc55cSDimitry Andric       Clear();
1290fe6060f1SDimitry Andric       return eKeyHandled;
1291fe6060f1SDimitry Andric     default:
1292fe6060f1SDimitry Andric       break;
1293fe6060f1SDimitry Andric     }
1294fe6060f1SDimitry Andric     return eKeyNotHandled;
1295fe6060f1SDimitry Andric   }
1296fe6060f1SDimitry Andric 
FieldDelegateHasError()1297fe6060f1SDimitry Andric   bool FieldDelegateHasError() override { return !m_error.empty(); }
1298fe6060f1SDimitry Andric 
FieldDelegateExitCallback()1299fe6060f1SDimitry Andric   void FieldDelegateExitCallback() override {
1300fe6060f1SDimitry Andric     if (!IsSpecified() && m_required)
1301fe6060f1SDimitry Andric       SetError("This field is required!");
1302fe6060f1SDimitry Andric   }
1303fe6060f1SDimitry Andric 
IsSpecified()1304fe6060f1SDimitry Andric   bool IsSpecified() { return !m_content.empty(); }
1305fe6060f1SDimitry Andric 
ClearError()1306fe6060f1SDimitry Andric   void ClearError() { m_error.clear(); }
1307fe6060f1SDimitry Andric 
GetError()1308fe6060f1SDimitry Andric   const std::string &GetError() { return m_error; }
1309fe6060f1SDimitry Andric 
SetError(const char * error)1310fe6060f1SDimitry Andric   void SetError(const char *error) { m_error = error; }
1311fe6060f1SDimitry Andric 
GetText()1312fe6060f1SDimitry Andric   const std::string &GetText() { return m_content; }
1313fe6060f1SDimitry Andric 
SetText(const char * text)1314349cc55cSDimitry Andric   void SetText(const char *text) {
1315349cc55cSDimitry Andric     if (text == nullptr) {
1316349cc55cSDimitry Andric       m_content.clear();
1317349cc55cSDimitry Andric       return;
1318349cc55cSDimitry Andric     }
1319349cc55cSDimitry Andric     m_content = text;
1320349cc55cSDimitry Andric   }
1321349cc55cSDimitry Andric 
1322fe6060f1SDimitry Andric protected:
1323fe6060f1SDimitry Andric   std::string m_label;
1324fe6060f1SDimitry Andric   bool m_required;
1325fe6060f1SDimitry Andric   // The position of the top left corner character of the border.
1326fe6060f1SDimitry Andric   std::string m_content;
1327fe6060f1SDimitry Andric   // The cursor position in the content string itself. Can be in the range
1328fe6060f1SDimitry Andric   // [0, GetContentLength()].
132981ad6265SDimitry Andric   int m_cursor_position = 0;
1330fe6060f1SDimitry Andric   // The index of the first visible character in the content.
133181ad6265SDimitry Andric   int m_first_visibile_char = 0;
1332fe6060f1SDimitry Andric   // Optional error message. If empty, field is considered to have no error.
1333fe6060f1SDimitry Andric   std::string m_error;
1334fe6060f1SDimitry Andric };
1335fe6060f1SDimitry Andric 
1336fe6060f1SDimitry Andric class IntegerFieldDelegate : public TextFieldDelegate {
1337fe6060f1SDimitry Andric public:
IntegerFieldDelegate(const char * label,int content,bool required)1338fe6060f1SDimitry Andric   IntegerFieldDelegate(const char *label, int content, bool required)
1339fe6060f1SDimitry Andric       : TextFieldDelegate(label, std::to_string(content).c_str(), required) {}
1340fe6060f1SDimitry Andric 
1341fe6060f1SDimitry Andric   // Only accept digits.
IsAcceptableChar(int key)1342fe6060f1SDimitry Andric   bool IsAcceptableChar(int key) override { return isdigit(key); }
1343fe6060f1SDimitry Andric 
1344fe6060f1SDimitry Andric   // Returns the integer content of the field.
GetInteger()1345fe6060f1SDimitry Andric   int GetInteger() { return std::stoi(m_content); }
1346fe6060f1SDimitry Andric };
1347fe6060f1SDimitry Andric 
1348fe6060f1SDimitry Andric class FileFieldDelegate : public TextFieldDelegate {
1349fe6060f1SDimitry Andric public:
FileFieldDelegate(const char * label,const char * content,bool need_to_exist,bool required)1350fe6060f1SDimitry Andric   FileFieldDelegate(const char *label, const char *content, bool need_to_exist,
1351fe6060f1SDimitry Andric                     bool required)
1352fe6060f1SDimitry Andric       : TextFieldDelegate(label, content, required),
1353fe6060f1SDimitry Andric         m_need_to_exist(need_to_exist) {}
1354fe6060f1SDimitry Andric 
FieldDelegateExitCallback()1355fe6060f1SDimitry Andric   void FieldDelegateExitCallback() override {
1356fe6060f1SDimitry Andric     TextFieldDelegate::FieldDelegateExitCallback();
1357fe6060f1SDimitry Andric     if (!IsSpecified())
1358fe6060f1SDimitry Andric       return;
1359fe6060f1SDimitry Andric 
1360fe6060f1SDimitry Andric     if (!m_need_to_exist)
1361fe6060f1SDimitry Andric       return;
1362fe6060f1SDimitry Andric 
1363fe6060f1SDimitry Andric     FileSpec file = GetResolvedFileSpec();
1364fe6060f1SDimitry Andric     if (!FileSystem::Instance().Exists(file)) {
1365fe6060f1SDimitry Andric       SetError("File doesn't exist!");
1366fe6060f1SDimitry Andric       return;
1367fe6060f1SDimitry Andric     }
1368fe6060f1SDimitry Andric     if (FileSystem::Instance().IsDirectory(file)) {
1369fe6060f1SDimitry Andric       SetError("Not a file!");
1370fe6060f1SDimitry Andric       return;
1371fe6060f1SDimitry Andric     }
1372fe6060f1SDimitry Andric   }
1373fe6060f1SDimitry Andric 
GetFileSpec()1374fe6060f1SDimitry Andric   FileSpec GetFileSpec() {
1375fe6060f1SDimitry Andric     FileSpec file_spec(GetPath());
1376fe6060f1SDimitry Andric     return file_spec;
1377fe6060f1SDimitry Andric   }
1378fe6060f1SDimitry Andric 
GetResolvedFileSpec()1379fe6060f1SDimitry Andric   FileSpec GetResolvedFileSpec() {
1380fe6060f1SDimitry Andric     FileSpec file_spec(GetPath());
1381fe6060f1SDimitry Andric     FileSystem::Instance().Resolve(file_spec);
1382fe6060f1SDimitry Andric     return file_spec;
1383fe6060f1SDimitry Andric   }
1384fe6060f1SDimitry Andric 
GetPath()1385fe6060f1SDimitry Andric   const std::string &GetPath() { return m_content; }
1386fe6060f1SDimitry Andric 
1387fe6060f1SDimitry Andric protected:
1388fe6060f1SDimitry Andric   bool m_need_to_exist;
1389fe6060f1SDimitry Andric };
1390fe6060f1SDimitry Andric 
1391fe6060f1SDimitry Andric class DirectoryFieldDelegate : public TextFieldDelegate {
1392fe6060f1SDimitry Andric public:
DirectoryFieldDelegate(const char * label,const char * content,bool need_to_exist,bool required)1393fe6060f1SDimitry Andric   DirectoryFieldDelegate(const char *label, const char *content,
1394fe6060f1SDimitry Andric                          bool need_to_exist, bool required)
1395fe6060f1SDimitry Andric       : TextFieldDelegate(label, content, required),
1396fe6060f1SDimitry Andric         m_need_to_exist(need_to_exist) {}
1397fe6060f1SDimitry Andric 
FieldDelegateExitCallback()1398fe6060f1SDimitry Andric   void FieldDelegateExitCallback() override {
1399fe6060f1SDimitry Andric     TextFieldDelegate::FieldDelegateExitCallback();
1400fe6060f1SDimitry Andric     if (!IsSpecified())
1401fe6060f1SDimitry Andric       return;
1402fe6060f1SDimitry Andric 
1403fe6060f1SDimitry Andric     if (!m_need_to_exist)
1404fe6060f1SDimitry Andric       return;
1405fe6060f1SDimitry Andric 
1406fe6060f1SDimitry Andric     FileSpec file = GetResolvedFileSpec();
1407fe6060f1SDimitry Andric     if (!FileSystem::Instance().Exists(file)) {
1408fe6060f1SDimitry Andric       SetError("Directory doesn't exist!");
1409fe6060f1SDimitry Andric       return;
1410fe6060f1SDimitry Andric     }
1411fe6060f1SDimitry Andric     if (!FileSystem::Instance().IsDirectory(file)) {
1412fe6060f1SDimitry Andric       SetError("Not a directory!");
1413fe6060f1SDimitry Andric       return;
1414fe6060f1SDimitry Andric     }
1415fe6060f1SDimitry Andric   }
1416fe6060f1SDimitry Andric 
GetFileSpec()1417fe6060f1SDimitry Andric   FileSpec GetFileSpec() {
1418fe6060f1SDimitry Andric     FileSpec file_spec(GetPath());
1419fe6060f1SDimitry Andric     return file_spec;
1420fe6060f1SDimitry Andric   }
1421fe6060f1SDimitry Andric 
GetResolvedFileSpec()1422fe6060f1SDimitry Andric   FileSpec GetResolvedFileSpec() {
1423fe6060f1SDimitry Andric     FileSpec file_spec(GetPath());
1424fe6060f1SDimitry Andric     FileSystem::Instance().Resolve(file_spec);
1425fe6060f1SDimitry Andric     return file_spec;
1426fe6060f1SDimitry Andric   }
1427fe6060f1SDimitry Andric 
GetPath()1428fe6060f1SDimitry Andric   const std::string &GetPath() { return m_content; }
1429fe6060f1SDimitry Andric 
1430fe6060f1SDimitry Andric protected:
1431fe6060f1SDimitry Andric   bool m_need_to_exist;
1432fe6060f1SDimitry Andric };
1433fe6060f1SDimitry Andric 
1434fe6060f1SDimitry Andric class ArchFieldDelegate : public TextFieldDelegate {
1435fe6060f1SDimitry Andric public:
ArchFieldDelegate(const char * label,const char * content,bool required)1436fe6060f1SDimitry Andric   ArchFieldDelegate(const char *label, const char *content, bool required)
1437fe6060f1SDimitry Andric       : TextFieldDelegate(label, content, required) {}
1438fe6060f1SDimitry Andric 
FieldDelegateExitCallback()1439fe6060f1SDimitry Andric   void FieldDelegateExitCallback() override {
1440fe6060f1SDimitry Andric     TextFieldDelegate::FieldDelegateExitCallback();
1441fe6060f1SDimitry Andric     if (!IsSpecified())
1442fe6060f1SDimitry Andric       return;
1443fe6060f1SDimitry Andric 
1444fe6060f1SDimitry Andric     if (!GetArchSpec().IsValid())
1445fe6060f1SDimitry Andric       SetError("Not a valid arch!");
1446fe6060f1SDimitry Andric   }
1447fe6060f1SDimitry Andric 
GetArchString()1448fe6060f1SDimitry Andric   const std::string &GetArchString() { return m_content; }
1449fe6060f1SDimitry Andric 
GetArchSpec()1450fe6060f1SDimitry Andric   ArchSpec GetArchSpec() { return ArchSpec(GetArchString()); }
1451fe6060f1SDimitry Andric };
1452fe6060f1SDimitry Andric 
1453fe6060f1SDimitry Andric class BooleanFieldDelegate : public FieldDelegate {
1454fe6060f1SDimitry Andric public:
BooleanFieldDelegate(const char * label,bool content)1455fe6060f1SDimitry Andric   BooleanFieldDelegate(const char *label, bool content)
1456fe6060f1SDimitry Andric       : m_label(label), m_content(content) {}
1457fe6060f1SDimitry Andric 
1458fe6060f1SDimitry Andric   // Boolean fields are drawn as checkboxes.
1459fe6060f1SDimitry Andric   //
1460fe6060f1SDimitry Andric   // [X] Label  or [ ] Label
1461fe6060f1SDimitry Andric 
1462fe6060f1SDimitry Andric   // Boolean fields are have a single line.
FieldDelegateGetHeight()1463fe6060f1SDimitry Andric   int FieldDelegateGetHeight() override { return 1; }
1464fe6060f1SDimitry Andric 
FieldDelegateDraw(Surface & surface,bool is_selected)1465349cc55cSDimitry Andric   void FieldDelegateDraw(Surface &surface, bool is_selected) override {
1466fe6060f1SDimitry Andric     surface.MoveCursor(0, 0);
1467fe6060f1SDimitry Andric     surface.PutChar('[');
1468fe6060f1SDimitry Andric     if (is_selected)
1469fe6060f1SDimitry Andric       surface.AttributeOn(A_REVERSE);
1470fe6060f1SDimitry Andric     surface.PutChar(m_content ? ACS_DIAMOND : ' ');
1471fe6060f1SDimitry Andric     if (is_selected)
1472fe6060f1SDimitry Andric       surface.AttributeOff(A_REVERSE);
1473fe6060f1SDimitry Andric     surface.PutChar(']');
1474fe6060f1SDimitry Andric     surface.PutChar(' ');
1475fe6060f1SDimitry Andric     surface.PutCString(m_label.c_str());
1476fe6060f1SDimitry Andric   }
1477fe6060f1SDimitry Andric 
ToggleContent()1478fe6060f1SDimitry Andric   void ToggleContent() { m_content = !m_content; }
1479fe6060f1SDimitry Andric 
SetContentToTrue()1480fe6060f1SDimitry Andric   void SetContentToTrue() { m_content = true; }
1481fe6060f1SDimitry Andric 
SetContentToFalse()1482fe6060f1SDimitry Andric   void SetContentToFalse() { m_content = false; }
1483fe6060f1SDimitry Andric 
FieldDelegateHandleChar(int key)1484fe6060f1SDimitry Andric   HandleCharResult FieldDelegateHandleChar(int key) override {
1485fe6060f1SDimitry Andric     switch (key) {
1486fe6060f1SDimitry Andric     case 't':
1487fe6060f1SDimitry Andric     case '1':
1488fe6060f1SDimitry Andric       SetContentToTrue();
1489fe6060f1SDimitry Andric       return eKeyHandled;
1490fe6060f1SDimitry Andric     case 'f':
1491fe6060f1SDimitry Andric     case '0':
1492fe6060f1SDimitry Andric       SetContentToFalse();
1493fe6060f1SDimitry Andric       return eKeyHandled;
1494fe6060f1SDimitry Andric     case ' ':
1495fe6060f1SDimitry Andric     case '\r':
1496fe6060f1SDimitry Andric     case '\n':
1497fe6060f1SDimitry Andric     case KEY_ENTER:
1498fe6060f1SDimitry Andric       ToggleContent();
1499fe6060f1SDimitry Andric       return eKeyHandled;
1500fe6060f1SDimitry Andric     default:
1501fe6060f1SDimitry Andric       break;
1502fe6060f1SDimitry Andric     }
1503fe6060f1SDimitry Andric     return eKeyNotHandled;
1504fe6060f1SDimitry Andric   }
1505fe6060f1SDimitry Andric 
1506fe6060f1SDimitry Andric   // Returns the boolean content of the field.
GetBoolean()1507fe6060f1SDimitry Andric   bool GetBoolean() { return m_content; }
1508fe6060f1SDimitry Andric 
1509fe6060f1SDimitry Andric protected:
1510fe6060f1SDimitry Andric   std::string m_label;
1511fe6060f1SDimitry Andric   bool m_content;
1512fe6060f1SDimitry Andric };
1513fe6060f1SDimitry Andric 
1514fe6060f1SDimitry Andric class ChoicesFieldDelegate : public FieldDelegate {
1515fe6060f1SDimitry Andric public:
ChoicesFieldDelegate(const char * label,int number_of_visible_choices,std::vector<std::string> choices)1516fe6060f1SDimitry Andric   ChoicesFieldDelegate(const char *label, int number_of_visible_choices,
1517fe6060f1SDimitry Andric                        std::vector<std::string> choices)
1518fe6060f1SDimitry Andric       : m_label(label), m_number_of_visible_choices(number_of_visible_choices),
151981ad6265SDimitry Andric         m_choices(choices) {}
1520fe6060f1SDimitry Andric 
1521fe6060f1SDimitry Andric   // Choices fields are drawn as titles boxses of a number of visible choices.
1522fe6060f1SDimitry Andric   // The rest of the choices become visible as the user scroll. The selected
1523fe6060f1SDimitry Andric   // choice is denoted by a diamond as the first character.
1524fe6060f1SDimitry Andric   //
1525fe6060f1SDimitry Andric   // __[Label]___________
1526fe6060f1SDimitry Andric   // |-Choice 1         |
1527fe6060f1SDimitry Andric   // | Choice 2         |
1528fe6060f1SDimitry Andric   // | Choice 3         |
1529fe6060f1SDimitry Andric   // |__________________|
1530fe6060f1SDimitry Andric 
1531fe6060f1SDimitry Andric   // Choices field have two border characters plus the number of visible
1532fe6060f1SDimitry Andric   // choices.
FieldDelegateGetHeight()1533fe6060f1SDimitry Andric   int FieldDelegateGetHeight() override {
1534fe6060f1SDimitry Andric     return m_number_of_visible_choices + 2;
1535fe6060f1SDimitry Andric   }
1536fe6060f1SDimitry Andric 
GetNumberOfChoices()1537fe6060f1SDimitry Andric   int GetNumberOfChoices() { return m_choices.size(); }
1538fe6060f1SDimitry Andric 
1539fe6060f1SDimitry Andric   // Get the index of the last visible choice.
GetLastVisibleChoice()1540fe6060f1SDimitry Andric   int GetLastVisibleChoice() {
1541fe6060f1SDimitry Andric     int index = m_first_visibile_choice + m_number_of_visible_choices;
1542fe6060f1SDimitry Andric     return std::min(index, GetNumberOfChoices()) - 1;
1543fe6060f1SDimitry Andric   }
1544fe6060f1SDimitry Andric 
DrawContent(Surface & surface,bool is_selected)1545349cc55cSDimitry Andric   void DrawContent(Surface &surface, bool is_selected) {
1546fe6060f1SDimitry Andric     int choices_to_draw = GetLastVisibleChoice() - m_first_visibile_choice + 1;
1547fe6060f1SDimitry Andric     for (int i = 0; i < choices_to_draw; i++) {
1548fe6060f1SDimitry Andric       surface.MoveCursor(0, i);
1549fe6060f1SDimitry Andric       int current_choice = m_first_visibile_choice + i;
1550fe6060f1SDimitry Andric       const char *text = m_choices[current_choice].c_str();
1551fe6060f1SDimitry Andric       bool highlight = is_selected && current_choice == m_choice;
1552fe6060f1SDimitry Andric       if (highlight)
1553fe6060f1SDimitry Andric         surface.AttributeOn(A_REVERSE);
1554fe6060f1SDimitry Andric       surface.PutChar(current_choice == m_choice ? ACS_DIAMOND : ' ');
1555fe6060f1SDimitry Andric       surface.PutCString(text);
1556fe6060f1SDimitry Andric       if (highlight)
1557fe6060f1SDimitry Andric         surface.AttributeOff(A_REVERSE);
1558fe6060f1SDimitry Andric     }
1559fe6060f1SDimitry Andric   }
1560fe6060f1SDimitry Andric 
FieldDelegateDraw(Surface & surface,bool is_selected)1561349cc55cSDimitry Andric   void FieldDelegateDraw(Surface &surface, bool is_selected) override {
1562fe6060f1SDimitry Andric     UpdateScrolling();
1563fe6060f1SDimitry Andric 
1564fe6060f1SDimitry Andric     surface.TitledBox(m_label.c_str());
1565fe6060f1SDimitry Andric 
1566fe6060f1SDimitry Andric     Rect content_bounds = surface.GetFrame();
1567fe6060f1SDimitry Andric     content_bounds.Inset(1, 1);
1568349cc55cSDimitry Andric     Surface content_surface = surface.SubSurface(content_bounds);
1569fe6060f1SDimitry Andric 
1570fe6060f1SDimitry Andric     DrawContent(content_surface, is_selected);
1571fe6060f1SDimitry Andric   }
1572fe6060f1SDimitry Andric 
SelectPrevious()1573fe6060f1SDimitry Andric   void SelectPrevious() {
1574fe6060f1SDimitry Andric     if (m_choice > 0)
1575fe6060f1SDimitry Andric       m_choice--;
1576fe6060f1SDimitry Andric   }
1577fe6060f1SDimitry Andric 
SelectNext()1578fe6060f1SDimitry Andric   void SelectNext() {
1579fe6060f1SDimitry Andric     if (m_choice < GetNumberOfChoices() - 1)
1580fe6060f1SDimitry Andric       m_choice++;
1581fe6060f1SDimitry Andric   }
1582fe6060f1SDimitry Andric 
UpdateScrolling()1583fe6060f1SDimitry Andric   void UpdateScrolling() {
1584fe6060f1SDimitry Andric     if (m_choice > GetLastVisibleChoice()) {
1585fe6060f1SDimitry Andric       m_first_visibile_choice = m_choice - (m_number_of_visible_choices - 1);
1586fe6060f1SDimitry Andric       return;
1587fe6060f1SDimitry Andric     }
1588fe6060f1SDimitry Andric 
1589fe6060f1SDimitry Andric     if (m_choice < m_first_visibile_choice)
1590fe6060f1SDimitry Andric       m_first_visibile_choice = m_choice;
1591fe6060f1SDimitry Andric   }
1592fe6060f1SDimitry Andric 
FieldDelegateHandleChar(int key)1593fe6060f1SDimitry Andric   HandleCharResult FieldDelegateHandleChar(int key) override {
1594fe6060f1SDimitry Andric     switch (key) {
1595fe6060f1SDimitry Andric     case KEY_UP:
1596fe6060f1SDimitry Andric       SelectPrevious();
1597fe6060f1SDimitry Andric       return eKeyHandled;
1598fe6060f1SDimitry Andric     case KEY_DOWN:
1599fe6060f1SDimitry Andric       SelectNext();
1600fe6060f1SDimitry Andric       return eKeyHandled;
1601fe6060f1SDimitry Andric     default:
1602fe6060f1SDimitry Andric       break;
1603fe6060f1SDimitry Andric     }
1604fe6060f1SDimitry Andric     return eKeyNotHandled;
1605fe6060f1SDimitry Andric   }
1606fe6060f1SDimitry Andric 
1607fe6060f1SDimitry Andric   // Returns the content of the choice as a string.
GetChoiceContent()1608fe6060f1SDimitry Andric   std::string GetChoiceContent() { return m_choices[m_choice]; }
1609fe6060f1SDimitry Andric 
1610fe6060f1SDimitry Andric   // Returns the index of the choice.
GetChoice()1611fe6060f1SDimitry Andric   int GetChoice() { return m_choice; }
1612fe6060f1SDimitry Andric 
SetChoice(llvm::StringRef choice)161381ad6265SDimitry Andric   void SetChoice(llvm::StringRef choice) {
1614fe6060f1SDimitry Andric     for (int i = 0; i < GetNumberOfChoices(); i++) {
1615fe6060f1SDimitry Andric       if (choice == m_choices[i]) {
1616fe6060f1SDimitry Andric         m_choice = i;
1617fe6060f1SDimitry Andric         return;
1618fe6060f1SDimitry Andric       }
1619fe6060f1SDimitry Andric     }
1620fe6060f1SDimitry Andric   }
1621fe6060f1SDimitry Andric 
1622fe6060f1SDimitry Andric protected:
1623fe6060f1SDimitry Andric   std::string m_label;
1624fe6060f1SDimitry Andric   int m_number_of_visible_choices;
1625fe6060f1SDimitry Andric   std::vector<std::string> m_choices;
1626fe6060f1SDimitry Andric   // The index of the selected choice.
162781ad6265SDimitry Andric   int m_choice = 0;
1628fe6060f1SDimitry Andric   // The index of the first visible choice in the field.
162981ad6265SDimitry Andric   int m_first_visibile_choice = 0;
1630fe6060f1SDimitry Andric };
1631fe6060f1SDimitry Andric 
1632fe6060f1SDimitry Andric class PlatformPluginFieldDelegate : public ChoicesFieldDelegate {
1633fe6060f1SDimitry Andric public:
PlatformPluginFieldDelegate(Debugger & debugger)1634fe6060f1SDimitry Andric   PlatformPluginFieldDelegate(Debugger &debugger)
1635fe6060f1SDimitry Andric       : ChoicesFieldDelegate("Platform Plugin", 3, GetPossiblePluginNames()) {
1636fe6060f1SDimitry Andric     PlatformSP platform_sp = debugger.GetPlatformList().GetSelectedPlatform();
1637fe6060f1SDimitry Andric     if (platform_sp)
163881ad6265SDimitry Andric       SetChoice(platform_sp->GetPluginName());
1639fe6060f1SDimitry Andric   }
1640fe6060f1SDimitry Andric 
GetPossiblePluginNames()1641fe6060f1SDimitry Andric   std::vector<std::string> GetPossiblePluginNames() {
1642fe6060f1SDimitry Andric     std::vector<std::string> names;
1643fe6060f1SDimitry Andric     size_t i = 0;
1644349cc55cSDimitry Andric     for (llvm::StringRef name =
1645349cc55cSDimitry Andric              PluginManager::GetPlatformPluginNameAtIndex(i++);
1646349cc55cSDimitry Andric          !name.empty(); name = PluginManager::GetProcessPluginNameAtIndex(i++))
1647349cc55cSDimitry Andric       names.push_back(name.str());
1648fe6060f1SDimitry Andric     return names;
1649fe6060f1SDimitry Andric   }
1650fe6060f1SDimitry Andric 
GetPluginName()1651fe6060f1SDimitry Andric   std::string GetPluginName() {
1652fe6060f1SDimitry Andric     std::string plugin_name = GetChoiceContent();
1653fe6060f1SDimitry Andric     return plugin_name;
1654fe6060f1SDimitry Andric   }
1655fe6060f1SDimitry Andric };
1656fe6060f1SDimitry Andric 
1657fe6060f1SDimitry Andric class ProcessPluginFieldDelegate : public ChoicesFieldDelegate {
1658fe6060f1SDimitry Andric public:
ProcessPluginFieldDelegate()1659fe6060f1SDimitry Andric   ProcessPluginFieldDelegate()
1660fe6060f1SDimitry Andric       : ChoicesFieldDelegate("Process Plugin", 3, GetPossiblePluginNames()) {}
1661fe6060f1SDimitry Andric 
GetPossiblePluginNames()1662fe6060f1SDimitry Andric   std::vector<std::string> GetPossiblePluginNames() {
1663fe6060f1SDimitry Andric     std::vector<std::string> names;
1664fe6060f1SDimitry Andric     names.push_back("<default>");
1665fe6060f1SDimitry Andric 
1666fe6060f1SDimitry Andric     size_t i = 0;
1667349cc55cSDimitry Andric     for (llvm::StringRef name = PluginManager::GetProcessPluginNameAtIndex(i++);
1668349cc55cSDimitry Andric          !name.empty(); name = PluginManager::GetProcessPluginNameAtIndex(i++))
1669349cc55cSDimitry Andric       names.push_back(name.str());
1670fe6060f1SDimitry Andric     return names;
1671fe6060f1SDimitry Andric   }
1672fe6060f1SDimitry Andric 
GetPluginName()1673fe6060f1SDimitry Andric   std::string GetPluginName() {
1674fe6060f1SDimitry Andric     std::string plugin_name = GetChoiceContent();
1675fe6060f1SDimitry Andric     if (plugin_name == "<default>")
1676fe6060f1SDimitry Andric       return "";
1677fe6060f1SDimitry Andric     return plugin_name;
1678fe6060f1SDimitry Andric   }
1679fe6060f1SDimitry Andric };
1680fe6060f1SDimitry Andric 
1681349cc55cSDimitry Andric class LazyBooleanFieldDelegate : public ChoicesFieldDelegate {
1682349cc55cSDimitry Andric public:
LazyBooleanFieldDelegate(const char * label,const char * calculate_label)1683349cc55cSDimitry Andric   LazyBooleanFieldDelegate(const char *label, const char *calculate_label)
1684349cc55cSDimitry Andric       : ChoicesFieldDelegate(label, 3, GetPossibleOptions(calculate_label)) {}
1685349cc55cSDimitry Andric 
1686349cc55cSDimitry Andric   static constexpr const char *kNo = "No";
1687349cc55cSDimitry Andric   static constexpr const char *kYes = "Yes";
1688349cc55cSDimitry Andric 
GetPossibleOptions(const char * calculate_label)1689349cc55cSDimitry Andric   std::vector<std::string> GetPossibleOptions(const char *calculate_label) {
1690349cc55cSDimitry Andric     std::vector<std::string> options;
1691349cc55cSDimitry Andric     options.push_back(calculate_label);
1692349cc55cSDimitry Andric     options.push_back(kYes);
1693349cc55cSDimitry Andric     options.push_back(kNo);
1694349cc55cSDimitry Andric     return options;
1695349cc55cSDimitry Andric   }
1696349cc55cSDimitry Andric 
GetLazyBoolean()1697349cc55cSDimitry Andric   LazyBool GetLazyBoolean() {
1698349cc55cSDimitry Andric     std::string choice = GetChoiceContent();
1699349cc55cSDimitry Andric     if (choice == kNo)
1700349cc55cSDimitry Andric       return eLazyBoolNo;
1701349cc55cSDimitry Andric     else if (choice == kYes)
1702349cc55cSDimitry Andric       return eLazyBoolYes;
1703349cc55cSDimitry Andric     else
1704349cc55cSDimitry Andric       return eLazyBoolCalculate;
1705349cc55cSDimitry Andric   }
1706349cc55cSDimitry Andric };
1707349cc55cSDimitry Andric 
1708fe6060f1SDimitry Andric template <class T> class ListFieldDelegate : public FieldDelegate {
1709fe6060f1SDimitry Andric public:
ListFieldDelegate(const char * label,T default_field)1710fe6060f1SDimitry Andric   ListFieldDelegate(const char *label, T default_field)
171181ad6265SDimitry Andric       : m_label(label), m_default_field(default_field),
1712fe6060f1SDimitry Andric         m_selection_type(SelectionType::NewButton) {}
1713fe6060f1SDimitry Andric 
1714fe6060f1SDimitry Andric   // Signify which element is selected. If a field or a remove button is
1715fe6060f1SDimitry Andric   // selected, then m_selection_index signifies the particular field that
1716fe6060f1SDimitry Andric   // is selected or the field that the remove button belongs to.
1717fe6060f1SDimitry Andric   enum class SelectionType { Field, RemoveButton, NewButton };
1718fe6060f1SDimitry Andric 
1719fe6060f1SDimitry Andric   // A List field is drawn as a titled box of a number of other fields of the
1720fe6060f1SDimitry Andric   // same type. Each field has a Remove button next to it that removes the
1721fe6060f1SDimitry Andric   // corresponding field. Finally, the last line contains a New button to add a
1722fe6060f1SDimitry Andric   // new field.
1723fe6060f1SDimitry Andric   //
1724fe6060f1SDimitry Andric   // __[Label]___________
1725fe6060f1SDimitry Andric   // | Field 0 [Remove] |
1726fe6060f1SDimitry Andric   // | Field 1 [Remove] |
1727fe6060f1SDimitry Andric   // | Field 2 [Remove] |
1728fe6060f1SDimitry Andric   // |      [New]       |
1729fe6060f1SDimitry Andric   // |__________________|
1730fe6060f1SDimitry Andric 
1731fe6060f1SDimitry Andric   // List fields have two lines for border characters, 1 line for the New
1732fe6060f1SDimitry Andric   // button, and the total height of the available fields.
FieldDelegateGetHeight()1733fe6060f1SDimitry Andric   int FieldDelegateGetHeight() override {
1734fe6060f1SDimitry Andric     // 2 border characters.
1735fe6060f1SDimitry Andric     int height = 2;
1736fe6060f1SDimitry Andric     // Total height of the fields.
1737fe6060f1SDimitry Andric     for (int i = 0; i < GetNumberOfFields(); i++) {
1738fe6060f1SDimitry Andric       height += m_fields[i].FieldDelegateGetHeight();
1739fe6060f1SDimitry Andric     }
1740fe6060f1SDimitry Andric     // A line for the New button.
1741fe6060f1SDimitry Andric     height++;
1742fe6060f1SDimitry Andric     return height;
1743fe6060f1SDimitry Andric   }
1744fe6060f1SDimitry Andric 
FieldDelegateGetScrollContext()1745fe6060f1SDimitry Andric   ScrollContext FieldDelegateGetScrollContext() override {
1746fe6060f1SDimitry Andric     int height = FieldDelegateGetHeight();
1747fe6060f1SDimitry Andric     if (m_selection_type == SelectionType::NewButton)
1748fe6060f1SDimitry Andric       return ScrollContext(height - 2, height - 1);
1749fe6060f1SDimitry Andric 
1750fe6060f1SDimitry Andric     FieldDelegate &field = m_fields[m_selection_index];
1751fe6060f1SDimitry Andric     ScrollContext context = field.FieldDelegateGetScrollContext();
1752fe6060f1SDimitry Andric 
1753fe6060f1SDimitry Andric     // Start at 1 because of the top border.
1754fe6060f1SDimitry Andric     int offset = 1;
1755fe6060f1SDimitry Andric     for (int i = 0; i < m_selection_index; i++) {
1756fe6060f1SDimitry Andric       offset += m_fields[i].FieldDelegateGetHeight();
1757fe6060f1SDimitry Andric     }
1758fe6060f1SDimitry Andric     context.Offset(offset);
1759fe6060f1SDimitry Andric 
1760fe6060f1SDimitry Andric     // If the scroll context is touching the top border, include it in the
1761fe6060f1SDimitry Andric     // context to show the label.
1762fe6060f1SDimitry Andric     if (context.start == 1)
1763fe6060f1SDimitry Andric       context.start--;
1764fe6060f1SDimitry Andric 
1765fe6060f1SDimitry Andric     // If the scroll context is touching the new button, include it as well as
1766fe6060f1SDimitry Andric     // the bottom border in the context.
1767fe6060f1SDimitry Andric     if (context.end == height - 3)
1768fe6060f1SDimitry Andric       context.end += 2;
1769fe6060f1SDimitry Andric 
1770fe6060f1SDimitry Andric     return context;
1771fe6060f1SDimitry Andric   }
1772fe6060f1SDimitry Andric 
DrawRemoveButton(Surface & surface,int highlight)1773349cc55cSDimitry Andric   void DrawRemoveButton(Surface &surface, int highlight) {
1774fe6060f1SDimitry Andric     surface.MoveCursor(1, surface.GetHeight() / 2);
1775fe6060f1SDimitry Andric     if (highlight)
1776fe6060f1SDimitry Andric       surface.AttributeOn(A_REVERSE);
1777fe6060f1SDimitry Andric     surface.PutCString("[Remove]");
1778fe6060f1SDimitry Andric     if (highlight)
1779fe6060f1SDimitry Andric       surface.AttributeOff(A_REVERSE);
1780fe6060f1SDimitry Andric   }
1781fe6060f1SDimitry Andric 
DrawFields(Surface & surface,bool is_selected)1782349cc55cSDimitry Andric   void DrawFields(Surface &surface, bool is_selected) {
1783fe6060f1SDimitry Andric     int line = 0;
1784fe6060f1SDimitry Andric     int width = surface.GetWidth();
1785fe6060f1SDimitry Andric     for (int i = 0; i < GetNumberOfFields(); i++) {
1786fe6060f1SDimitry Andric       int height = m_fields[i].FieldDelegateGetHeight();
1787fe6060f1SDimitry Andric       Rect bounds = Rect(Point(0, line), Size(width, height));
1788fe6060f1SDimitry Andric       Rect field_bounds, remove_button_bounds;
1789fe6060f1SDimitry Andric       bounds.VerticalSplit(bounds.size.width - sizeof(" [Remove]"),
1790fe6060f1SDimitry Andric                            field_bounds, remove_button_bounds);
1791349cc55cSDimitry Andric       Surface field_surface = surface.SubSurface(field_bounds);
1792349cc55cSDimitry Andric       Surface remove_button_surface = surface.SubSurface(remove_button_bounds);
1793fe6060f1SDimitry Andric 
1794fe6060f1SDimitry Andric       bool is_element_selected = m_selection_index == i && is_selected;
1795fe6060f1SDimitry Andric       bool is_field_selected =
1796fe6060f1SDimitry Andric           is_element_selected && m_selection_type == SelectionType::Field;
1797fe6060f1SDimitry Andric       bool is_remove_button_selected =
1798fe6060f1SDimitry Andric           is_element_selected &&
1799fe6060f1SDimitry Andric           m_selection_type == SelectionType::RemoveButton;
1800fe6060f1SDimitry Andric       m_fields[i].FieldDelegateDraw(field_surface, is_field_selected);
1801fe6060f1SDimitry Andric       DrawRemoveButton(remove_button_surface, is_remove_button_selected);
1802fe6060f1SDimitry Andric 
1803fe6060f1SDimitry Andric       line += height;
1804fe6060f1SDimitry Andric     }
1805fe6060f1SDimitry Andric   }
1806fe6060f1SDimitry Andric 
DrawNewButton(Surface & surface,bool is_selected)1807349cc55cSDimitry Andric   void DrawNewButton(Surface &surface, bool is_selected) {
1808fe6060f1SDimitry Andric     const char *button_text = "[New]";
1809fe6060f1SDimitry Andric     int x = (surface.GetWidth() - sizeof(button_text) - 1) / 2;
1810fe6060f1SDimitry Andric     surface.MoveCursor(x, 0);
1811fe6060f1SDimitry Andric     bool highlight =
1812fe6060f1SDimitry Andric         is_selected && m_selection_type == SelectionType::NewButton;
1813fe6060f1SDimitry Andric     if (highlight)
1814fe6060f1SDimitry Andric       surface.AttributeOn(A_REVERSE);
1815fe6060f1SDimitry Andric     surface.PutCString(button_text);
1816fe6060f1SDimitry Andric     if (highlight)
1817fe6060f1SDimitry Andric       surface.AttributeOff(A_REVERSE);
1818fe6060f1SDimitry Andric   }
1819fe6060f1SDimitry Andric 
FieldDelegateDraw(Surface & surface,bool is_selected)1820349cc55cSDimitry Andric   void FieldDelegateDraw(Surface &surface, bool is_selected) override {
1821fe6060f1SDimitry Andric     surface.TitledBox(m_label.c_str());
1822fe6060f1SDimitry Andric 
1823fe6060f1SDimitry Andric     Rect content_bounds = surface.GetFrame();
1824fe6060f1SDimitry Andric     content_bounds.Inset(1, 1);
1825fe6060f1SDimitry Andric     Rect fields_bounds, new_button_bounds;
1826fe6060f1SDimitry Andric     content_bounds.HorizontalSplit(content_bounds.size.height - 1,
1827fe6060f1SDimitry Andric                                    fields_bounds, new_button_bounds);
1828349cc55cSDimitry Andric     Surface fields_surface = surface.SubSurface(fields_bounds);
1829349cc55cSDimitry Andric     Surface new_button_surface = surface.SubSurface(new_button_bounds);
1830fe6060f1SDimitry Andric 
1831fe6060f1SDimitry Andric     DrawFields(fields_surface, is_selected);
1832fe6060f1SDimitry Andric     DrawNewButton(new_button_surface, is_selected);
1833fe6060f1SDimitry Andric   }
1834fe6060f1SDimitry Andric 
AddNewField()1835fe6060f1SDimitry Andric   void AddNewField() {
1836fe6060f1SDimitry Andric     m_fields.push_back(m_default_field);
1837fe6060f1SDimitry Andric     m_selection_index = GetNumberOfFields() - 1;
1838fe6060f1SDimitry Andric     m_selection_type = SelectionType::Field;
1839fe6060f1SDimitry Andric     FieldDelegate &field = m_fields[m_selection_index];
1840fe6060f1SDimitry Andric     field.FieldDelegateSelectFirstElement();
1841fe6060f1SDimitry Andric   }
1842fe6060f1SDimitry Andric 
RemoveField()1843fe6060f1SDimitry Andric   void RemoveField() {
1844fe6060f1SDimitry Andric     m_fields.erase(m_fields.begin() + m_selection_index);
1845fe6060f1SDimitry Andric     if (m_selection_index != 0)
1846fe6060f1SDimitry Andric       m_selection_index--;
1847fe6060f1SDimitry Andric 
1848fe6060f1SDimitry Andric     if (GetNumberOfFields() > 0) {
1849fe6060f1SDimitry Andric       m_selection_type = SelectionType::Field;
1850fe6060f1SDimitry Andric       FieldDelegate &field = m_fields[m_selection_index];
1851fe6060f1SDimitry Andric       field.FieldDelegateSelectFirstElement();
1852fe6060f1SDimitry Andric     } else
1853fe6060f1SDimitry Andric       m_selection_type = SelectionType::NewButton;
1854fe6060f1SDimitry Andric   }
1855fe6060f1SDimitry Andric 
SelectNext(int key)1856fe6060f1SDimitry Andric   HandleCharResult SelectNext(int key) {
1857fe6060f1SDimitry Andric     if (m_selection_type == SelectionType::NewButton)
1858fe6060f1SDimitry Andric       return eKeyNotHandled;
1859fe6060f1SDimitry Andric 
1860fe6060f1SDimitry Andric     if (m_selection_type == SelectionType::RemoveButton) {
1861fe6060f1SDimitry Andric       if (m_selection_index == GetNumberOfFields() - 1) {
1862fe6060f1SDimitry Andric         m_selection_type = SelectionType::NewButton;
1863fe6060f1SDimitry Andric         return eKeyHandled;
1864fe6060f1SDimitry Andric       }
1865fe6060f1SDimitry Andric       m_selection_index++;
1866fe6060f1SDimitry Andric       m_selection_type = SelectionType::Field;
1867fe6060f1SDimitry Andric       FieldDelegate &next_field = m_fields[m_selection_index];
1868fe6060f1SDimitry Andric       next_field.FieldDelegateSelectFirstElement();
1869fe6060f1SDimitry Andric       return eKeyHandled;
1870fe6060f1SDimitry Andric     }
1871fe6060f1SDimitry Andric 
1872fe6060f1SDimitry Andric     FieldDelegate &field = m_fields[m_selection_index];
1873fe6060f1SDimitry Andric     if (!field.FieldDelegateOnLastOrOnlyElement()) {
1874fe6060f1SDimitry Andric       return field.FieldDelegateHandleChar(key);
1875fe6060f1SDimitry Andric     }
1876fe6060f1SDimitry Andric 
1877fe6060f1SDimitry Andric     field.FieldDelegateExitCallback();
1878fe6060f1SDimitry Andric 
1879fe6060f1SDimitry Andric     m_selection_type = SelectionType::RemoveButton;
1880fe6060f1SDimitry Andric     return eKeyHandled;
1881fe6060f1SDimitry Andric   }
1882fe6060f1SDimitry Andric 
SelectPrevious(int key)1883fe6060f1SDimitry Andric   HandleCharResult SelectPrevious(int key) {
1884fe6060f1SDimitry Andric     if (FieldDelegateOnFirstOrOnlyElement())
1885fe6060f1SDimitry Andric       return eKeyNotHandled;
1886fe6060f1SDimitry Andric 
1887fe6060f1SDimitry Andric     if (m_selection_type == SelectionType::RemoveButton) {
1888fe6060f1SDimitry Andric       m_selection_type = SelectionType::Field;
1889fe6060f1SDimitry Andric       FieldDelegate &field = m_fields[m_selection_index];
1890fe6060f1SDimitry Andric       field.FieldDelegateSelectLastElement();
1891fe6060f1SDimitry Andric       return eKeyHandled;
1892fe6060f1SDimitry Andric     }
1893fe6060f1SDimitry Andric 
1894fe6060f1SDimitry Andric     if (m_selection_type == SelectionType::NewButton) {
1895fe6060f1SDimitry Andric       m_selection_type = SelectionType::RemoveButton;
1896fe6060f1SDimitry Andric       m_selection_index = GetNumberOfFields() - 1;
1897fe6060f1SDimitry Andric       return eKeyHandled;
1898fe6060f1SDimitry Andric     }
1899fe6060f1SDimitry Andric 
1900fe6060f1SDimitry Andric     FieldDelegate &field = m_fields[m_selection_index];
1901fe6060f1SDimitry Andric     if (!field.FieldDelegateOnFirstOrOnlyElement()) {
1902fe6060f1SDimitry Andric       return field.FieldDelegateHandleChar(key);
1903fe6060f1SDimitry Andric     }
1904fe6060f1SDimitry Andric 
1905fe6060f1SDimitry Andric     field.FieldDelegateExitCallback();
1906fe6060f1SDimitry Andric 
1907fe6060f1SDimitry Andric     m_selection_type = SelectionType::RemoveButton;
1908fe6060f1SDimitry Andric     m_selection_index--;
1909fe6060f1SDimitry Andric     return eKeyHandled;
1910fe6060f1SDimitry Andric   }
1911fe6060f1SDimitry Andric 
1912349cc55cSDimitry Andric   // If the last element of the field is selected and it didn't handle the key.
1913349cc55cSDimitry Andric   // Select the next field or new button if the selected field is the last one.
SelectNextInList(int key)1914349cc55cSDimitry Andric   HandleCharResult SelectNextInList(int key) {
1915349cc55cSDimitry Andric     assert(m_selection_type == SelectionType::Field);
1916349cc55cSDimitry Andric 
1917349cc55cSDimitry Andric     FieldDelegate &field = m_fields[m_selection_index];
1918349cc55cSDimitry Andric     if (field.FieldDelegateHandleChar(key) == eKeyHandled)
1919349cc55cSDimitry Andric       return eKeyHandled;
1920349cc55cSDimitry Andric 
1921349cc55cSDimitry Andric     if (!field.FieldDelegateOnLastOrOnlyElement())
1922349cc55cSDimitry Andric       return eKeyNotHandled;
1923349cc55cSDimitry Andric 
1924349cc55cSDimitry Andric     field.FieldDelegateExitCallback();
1925349cc55cSDimitry Andric 
1926349cc55cSDimitry Andric     if (m_selection_index == GetNumberOfFields() - 1) {
1927349cc55cSDimitry Andric       m_selection_type = SelectionType::NewButton;
1928349cc55cSDimitry Andric       return eKeyHandled;
1929349cc55cSDimitry Andric     }
1930349cc55cSDimitry Andric 
1931349cc55cSDimitry Andric     m_selection_index++;
1932349cc55cSDimitry Andric     FieldDelegate &next_field = m_fields[m_selection_index];
1933349cc55cSDimitry Andric     next_field.FieldDelegateSelectFirstElement();
1934349cc55cSDimitry Andric     return eKeyHandled;
1935349cc55cSDimitry Andric   }
1936349cc55cSDimitry Andric 
FieldDelegateHandleChar(int key)1937fe6060f1SDimitry Andric   HandleCharResult FieldDelegateHandleChar(int key) override {
1938fe6060f1SDimitry Andric     switch (key) {
1939fe6060f1SDimitry Andric     case '\r':
1940fe6060f1SDimitry Andric     case '\n':
1941fe6060f1SDimitry Andric     case KEY_ENTER:
1942fe6060f1SDimitry Andric       switch (m_selection_type) {
1943fe6060f1SDimitry Andric       case SelectionType::NewButton:
1944fe6060f1SDimitry Andric         AddNewField();
1945fe6060f1SDimitry Andric         return eKeyHandled;
1946fe6060f1SDimitry Andric       case SelectionType::RemoveButton:
1947fe6060f1SDimitry Andric         RemoveField();
1948fe6060f1SDimitry Andric         return eKeyHandled;
1949349cc55cSDimitry Andric       case SelectionType::Field:
1950349cc55cSDimitry Andric         return SelectNextInList(key);
1951fe6060f1SDimitry Andric       }
1952fe6060f1SDimitry Andric       break;
1953fe6060f1SDimitry Andric     case '\t':
1954349cc55cSDimitry Andric       return SelectNext(key);
1955fe6060f1SDimitry Andric     case KEY_SHIFT_TAB:
1956349cc55cSDimitry Andric       return SelectPrevious(key);
1957fe6060f1SDimitry Andric     default:
1958fe6060f1SDimitry Andric       break;
1959fe6060f1SDimitry Andric     }
1960fe6060f1SDimitry Andric 
1961fe6060f1SDimitry Andric     // If the key wasn't handled and one of the fields is selected, pass the key
1962fe6060f1SDimitry Andric     // to that field.
1963fe6060f1SDimitry Andric     if (m_selection_type == SelectionType::Field) {
1964fe6060f1SDimitry Andric       return m_fields[m_selection_index].FieldDelegateHandleChar(key);
1965fe6060f1SDimitry Andric     }
1966fe6060f1SDimitry Andric 
1967fe6060f1SDimitry Andric     return eKeyNotHandled;
1968fe6060f1SDimitry Andric   }
1969fe6060f1SDimitry Andric 
FieldDelegateOnLastOrOnlyElement()1970fe6060f1SDimitry Andric   bool FieldDelegateOnLastOrOnlyElement() override {
1971fe6060f1SDimitry Andric     if (m_selection_type == SelectionType::NewButton) {
1972fe6060f1SDimitry Andric       return true;
1973fe6060f1SDimitry Andric     }
1974fe6060f1SDimitry Andric     return false;
1975fe6060f1SDimitry Andric   }
1976fe6060f1SDimitry Andric 
FieldDelegateOnFirstOrOnlyElement()1977fe6060f1SDimitry Andric   bool FieldDelegateOnFirstOrOnlyElement() override {
1978fe6060f1SDimitry Andric     if (m_selection_type == SelectionType::NewButton &&
1979fe6060f1SDimitry Andric         GetNumberOfFields() == 0)
1980fe6060f1SDimitry Andric       return true;
1981fe6060f1SDimitry Andric 
1982fe6060f1SDimitry Andric     if (m_selection_type == SelectionType::Field && m_selection_index == 0) {
1983fe6060f1SDimitry Andric       FieldDelegate &field = m_fields[m_selection_index];
1984fe6060f1SDimitry Andric       return field.FieldDelegateOnFirstOrOnlyElement();
1985fe6060f1SDimitry Andric     }
1986fe6060f1SDimitry Andric 
1987fe6060f1SDimitry Andric     return false;
1988fe6060f1SDimitry Andric   }
1989fe6060f1SDimitry Andric 
FieldDelegateSelectFirstElement()1990fe6060f1SDimitry Andric   void FieldDelegateSelectFirstElement() override {
1991fe6060f1SDimitry Andric     if (GetNumberOfFields() == 0) {
1992fe6060f1SDimitry Andric       m_selection_type = SelectionType::NewButton;
1993fe6060f1SDimitry Andric       return;
1994fe6060f1SDimitry Andric     }
1995fe6060f1SDimitry Andric 
1996fe6060f1SDimitry Andric     m_selection_type = SelectionType::Field;
1997fe6060f1SDimitry Andric     m_selection_index = 0;
1998fe6060f1SDimitry Andric   }
1999fe6060f1SDimitry Andric 
FieldDelegateSelectLastElement()2000fe6060f1SDimitry Andric   void FieldDelegateSelectLastElement() override {
2001fe6060f1SDimitry Andric     m_selection_type = SelectionType::NewButton;
2002fe6060f1SDimitry Andric   }
2003fe6060f1SDimitry Andric 
GetNumberOfFields()2004fe6060f1SDimitry Andric   int GetNumberOfFields() { return m_fields.size(); }
2005fe6060f1SDimitry Andric 
2006fe6060f1SDimitry Andric   // Returns the form delegate at the current index.
GetField(int index)2007fe6060f1SDimitry Andric   T &GetField(int index) { return m_fields[index]; }
2008fe6060f1SDimitry Andric 
2009fe6060f1SDimitry Andric protected:
2010fe6060f1SDimitry Andric   std::string m_label;
2011fe6060f1SDimitry Andric   // The default field delegate instance from which new field delegates will be
2012fe6060f1SDimitry Andric   // created though a copy.
2013fe6060f1SDimitry Andric   T m_default_field;
2014fe6060f1SDimitry Andric   std::vector<T> m_fields;
201581ad6265SDimitry Andric   int m_selection_index = 0;
2016fe6060f1SDimitry Andric   // See SelectionType class enum.
2017fe6060f1SDimitry Andric   SelectionType m_selection_type;
2018fe6060f1SDimitry Andric };
2019fe6060f1SDimitry Andric 
2020349cc55cSDimitry Andric class ArgumentsFieldDelegate : public ListFieldDelegate<TextFieldDelegate> {
2021349cc55cSDimitry Andric public:
ArgumentsFieldDelegate()2022349cc55cSDimitry Andric   ArgumentsFieldDelegate()
2023349cc55cSDimitry Andric       : ListFieldDelegate("Arguments",
2024349cc55cSDimitry Andric                           TextFieldDelegate("Argument", "", false)) {}
2025349cc55cSDimitry Andric 
GetArguments()2026349cc55cSDimitry Andric   Args GetArguments() {
2027349cc55cSDimitry Andric     Args arguments;
2028349cc55cSDimitry Andric     for (int i = 0; i < GetNumberOfFields(); i++) {
2029349cc55cSDimitry Andric       arguments.AppendArgument(GetField(i).GetText());
2030349cc55cSDimitry Andric     }
2031349cc55cSDimitry Andric     return arguments;
2032349cc55cSDimitry Andric   }
2033349cc55cSDimitry Andric 
AddArguments(const Args & arguments)2034349cc55cSDimitry Andric   void AddArguments(const Args &arguments) {
2035349cc55cSDimitry Andric     for (size_t i = 0; i < arguments.GetArgumentCount(); i++) {
2036349cc55cSDimitry Andric       AddNewField();
2037349cc55cSDimitry Andric       TextFieldDelegate &field = GetField(GetNumberOfFields() - 1);
2038349cc55cSDimitry Andric       field.SetText(arguments.GetArgumentAtIndex(i));
2039349cc55cSDimitry Andric     }
2040349cc55cSDimitry Andric   }
2041349cc55cSDimitry Andric };
2042349cc55cSDimitry Andric 
2043349cc55cSDimitry Andric template <class KeyFieldDelegateType, class ValueFieldDelegateType>
2044349cc55cSDimitry Andric class MappingFieldDelegate : public FieldDelegate {
2045349cc55cSDimitry Andric public:
MappingFieldDelegate(KeyFieldDelegateType key_field,ValueFieldDelegateType value_field)2046349cc55cSDimitry Andric   MappingFieldDelegate(KeyFieldDelegateType key_field,
2047349cc55cSDimitry Andric                        ValueFieldDelegateType value_field)
2048349cc55cSDimitry Andric       : m_key_field(key_field), m_value_field(value_field),
2049349cc55cSDimitry Andric         m_selection_type(SelectionType::Key) {}
2050349cc55cSDimitry Andric 
2051349cc55cSDimitry Andric   // Signify which element is selected. The key field or its value field.
2052349cc55cSDimitry Andric   enum class SelectionType { Key, Value };
2053349cc55cSDimitry Andric 
2054349cc55cSDimitry Andric   // A mapping field is drawn as two text fields with a right arrow in between.
2055349cc55cSDimitry Andric   // The first field stores the key of the mapping and the second stores the
2056349cc55cSDimitry Andric   // value if the mapping.
2057349cc55cSDimitry Andric   //
2058349cc55cSDimitry Andric   // __[Key]_____________   __[Value]___________
2059349cc55cSDimitry Andric   // |                  | > |                  |
2060349cc55cSDimitry Andric   // |__________________|   |__________________|
2061349cc55cSDimitry Andric   // - Error message if it exists.
2062349cc55cSDimitry Andric 
2063349cc55cSDimitry Andric   // The mapping field has a height that is equal to the maximum height between
2064349cc55cSDimitry Andric   // the key and value fields.
FieldDelegateGetHeight()2065349cc55cSDimitry Andric   int FieldDelegateGetHeight() override {
2066349cc55cSDimitry Andric     return std::max(m_key_field.FieldDelegateGetHeight(),
2067349cc55cSDimitry Andric                     m_value_field.FieldDelegateGetHeight());
2068349cc55cSDimitry Andric   }
2069349cc55cSDimitry Andric 
DrawArrow(Surface & surface)2070349cc55cSDimitry Andric   void DrawArrow(Surface &surface) {
2071349cc55cSDimitry Andric     surface.MoveCursor(0, 1);
2072349cc55cSDimitry Andric     surface.PutChar(ACS_RARROW);
2073349cc55cSDimitry Andric   }
2074349cc55cSDimitry Andric 
FieldDelegateDraw(Surface & surface,bool is_selected)2075349cc55cSDimitry Andric   void FieldDelegateDraw(Surface &surface, bool is_selected) override {
2076349cc55cSDimitry Andric     Rect bounds = surface.GetFrame();
2077349cc55cSDimitry Andric     Rect key_field_bounds, arrow_and_value_field_bounds;
2078349cc55cSDimitry Andric     bounds.VerticalSplit(bounds.size.width / 2, key_field_bounds,
2079349cc55cSDimitry Andric                          arrow_and_value_field_bounds);
2080349cc55cSDimitry Andric     Rect arrow_bounds, value_field_bounds;
2081349cc55cSDimitry Andric     arrow_and_value_field_bounds.VerticalSplit(1, arrow_bounds,
2082349cc55cSDimitry Andric                                                value_field_bounds);
2083349cc55cSDimitry Andric 
2084349cc55cSDimitry Andric     Surface key_field_surface = surface.SubSurface(key_field_bounds);
2085349cc55cSDimitry Andric     Surface arrow_surface = surface.SubSurface(arrow_bounds);
2086349cc55cSDimitry Andric     Surface value_field_surface = surface.SubSurface(value_field_bounds);
2087349cc55cSDimitry Andric 
2088349cc55cSDimitry Andric     bool key_is_selected =
2089349cc55cSDimitry Andric         m_selection_type == SelectionType::Key && is_selected;
2090349cc55cSDimitry Andric     m_key_field.FieldDelegateDraw(key_field_surface, key_is_selected);
2091349cc55cSDimitry Andric     DrawArrow(arrow_surface);
2092349cc55cSDimitry Andric     bool value_is_selected =
2093349cc55cSDimitry Andric         m_selection_type == SelectionType::Value && is_selected;
2094349cc55cSDimitry Andric     m_value_field.FieldDelegateDraw(value_field_surface, value_is_selected);
2095349cc55cSDimitry Andric   }
2096349cc55cSDimitry Andric 
SelectNext(int key)2097349cc55cSDimitry Andric   HandleCharResult SelectNext(int key) {
2098349cc55cSDimitry Andric     if (FieldDelegateOnLastOrOnlyElement())
2099349cc55cSDimitry Andric       return eKeyNotHandled;
2100349cc55cSDimitry Andric 
2101349cc55cSDimitry Andric     if (!m_key_field.FieldDelegateOnLastOrOnlyElement()) {
2102349cc55cSDimitry Andric       return m_key_field.FieldDelegateHandleChar(key);
2103349cc55cSDimitry Andric     }
2104349cc55cSDimitry Andric 
2105349cc55cSDimitry Andric     m_key_field.FieldDelegateExitCallback();
2106349cc55cSDimitry Andric     m_selection_type = SelectionType::Value;
2107349cc55cSDimitry Andric     m_value_field.FieldDelegateSelectFirstElement();
2108349cc55cSDimitry Andric     return eKeyHandled;
2109349cc55cSDimitry Andric   }
2110349cc55cSDimitry Andric 
SelectPrevious(int key)2111349cc55cSDimitry Andric   HandleCharResult SelectPrevious(int key) {
2112349cc55cSDimitry Andric     if (FieldDelegateOnFirstOrOnlyElement())
2113349cc55cSDimitry Andric       return eKeyNotHandled;
2114349cc55cSDimitry Andric 
2115349cc55cSDimitry Andric     if (!m_value_field.FieldDelegateOnFirstOrOnlyElement()) {
2116349cc55cSDimitry Andric       return m_value_field.FieldDelegateHandleChar(key);
2117349cc55cSDimitry Andric     }
2118349cc55cSDimitry Andric 
2119349cc55cSDimitry Andric     m_value_field.FieldDelegateExitCallback();
2120349cc55cSDimitry Andric     m_selection_type = SelectionType::Key;
2121349cc55cSDimitry Andric     m_key_field.FieldDelegateSelectLastElement();
2122349cc55cSDimitry Andric     return eKeyHandled;
2123349cc55cSDimitry Andric   }
2124349cc55cSDimitry Andric 
2125349cc55cSDimitry Andric   // If the value field is selected, pass the key to it. If the key field is
2126349cc55cSDimitry Andric   // selected, its last element is selected, and it didn't handle the key, then
2127349cc55cSDimitry Andric   // select its corresponding value field.
SelectNextField(int key)2128349cc55cSDimitry Andric   HandleCharResult SelectNextField(int key) {
2129349cc55cSDimitry Andric     if (m_selection_type == SelectionType::Value) {
2130349cc55cSDimitry Andric       return m_value_field.FieldDelegateHandleChar(key);
2131349cc55cSDimitry Andric     }
2132349cc55cSDimitry Andric 
2133349cc55cSDimitry Andric     if (m_key_field.FieldDelegateHandleChar(key) == eKeyHandled)
2134349cc55cSDimitry Andric       return eKeyHandled;
2135349cc55cSDimitry Andric 
2136349cc55cSDimitry Andric     if (!m_key_field.FieldDelegateOnLastOrOnlyElement())
2137349cc55cSDimitry Andric       return eKeyNotHandled;
2138349cc55cSDimitry Andric 
2139349cc55cSDimitry Andric     m_key_field.FieldDelegateExitCallback();
2140349cc55cSDimitry Andric     m_selection_type = SelectionType::Value;
2141349cc55cSDimitry Andric     m_value_field.FieldDelegateSelectFirstElement();
2142349cc55cSDimitry Andric     return eKeyHandled;
2143349cc55cSDimitry Andric   }
2144349cc55cSDimitry Andric 
FieldDelegateHandleChar(int key)2145349cc55cSDimitry Andric   HandleCharResult FieldDelegateHandleChar(int key) override {
2146349cc55cSDimitry Andric     switch (key) {
2147349cc55cSDimitry Andric     case KEY_RETURN:
2148349cc55cSDimitry Andric       return SelectNextField(key);
2149349cc55cSDimitry Andric     case '\t':
2150349cc55cSDimitry Andric       return SelectNext(key);
2151349cc55cSDimitry Andric     case KEY_SHIFT_TAB:
2152349cc55cSDimitry Andric       return SelectPrevious(key);
2153349cc55cSDimitry Andric     default:
2154349cc55cSDimitry Andric       break;
2155349cc55cSDimitry Andric     }
2156349cc55cSDimitry Andric 
2157349cc55cSDimitry Andric     // If the key wasn't handled, pass the key to the selected field.
2158349cc55cSDimitry Andric     if (m_selection_type == SelectionType::Key)
2159349cc55cSDimitry Andric       return m_key_field.FieldDelegateHandleChar(key);
2160349cc55cSDimitry Andric     else
2161349cc55cSDimitry Andric       return m_value_field.FieldDelegateHandleChar(key);
2162349cc55cSDimitry Andric 
2163349cc55cSDimitry Andric     return eKeyNotHandled;
2164349cc55cSDimitry Andric   }
2165349cc55cSDimitry Andric 
FieldDelegateOnFirstOrOnlyElement()2166349cc55cSDimitry Andric   bool FieldDelegateOnFirstOrOnlyElement() override {
2167349cc55cSDimitry Andric     return m_selection_type == SelectionType::Key;
2168349cc55cSDimitry Andric   }
2169349cc55cSDimitry Andric 
FieldDelegateOnLastOrOnlyElement()2170349cc55cSDimitry Andric   bool FieldDelegateOnLastOrOnlyElement() override {
2171349cc55cSDimitry Andric     return m_selection_type == SelectionType::Value;
2172349cc55cSDimitry Andric   }
2173349cc55cSDimitry Andric 
FieldDelegateSelectFirstElement()2174349cc55cSDimitry Andric   void FieldDelegateSelectFirstElement() override {
2175349cc55cSDimitry Andric     m_selection_type = SelectionType::Key;
2176349cc55cSDimitry Andric   }
2177349cc55cSDimitry Andric 
FieldDelegateSelectLastElement()2178349cc55cSDimitry Andric   void FieldDelegateSelectLastElement() override {
2179349cc55cSDimitry Andric     m_selection_type = SelectionType::Value;
2180349cc55cSDimitry Andric   }
2181349cc55cSDimitry Andric 
FieldDelegateHasError()2182349cc55cSDimitry Andric   bool FieldDelegateHasError() override {
2183349cc55cSDimitry Andric     return m_key_field.FieldDelegateHasError() ||
2184349cc55cSDimitry Andric            m_value_field.FieldDelegateHasError();
2185349cc55cSDimitry Andric   }
2186349cc55cSDimitry Andric 
GetKeyField()2187349cc55cSDimitry Andric   KeyFieldDelegateType &GetKeyField() { return m_key_field; }
2188349cc55cSDimitry Andric 
GetValueField()2189349cc55cSDimitry Andric   ValueFieldDelegateType &GetValueField() { return m_value_field; }
2190349cc55cSDimitry Andric 
2191349cc55cSDimitry Andric protected:
2192349cc55cSDimitry Andric   KeyFieldDelegateType m_key_field;
2193349cc55cSDimitry Andric   ValueFieldDelegateType m_value_field;
2194349cc55cSDimitry Andric   // See SelectionType class enum.
2195349cc55cSDimitry Andric   SelectionType m_selection_type;
2196349cc55cSDimitry Andric };
2197349cc55cSDimitry Andric 
2198349cc55cSDimitry Andric class EnvironmentVariableNameFieldDelegate : public TextFieldDelegate {
2199349cc55cSDimitry Andric public:
EnvironmentVariableNameFieldDelegate(const char * content)2200349cc55cSDimitry Andric   EnvironmentVariableNameFieldDelegate(const char *content)
2201349cc55cSDimitry Andric       : TextFieldDelegate("Name", content, true) {}
2202349cc55cSDimitry Andric 
2203349cc55cSDimitry Andric   // Environment variable names can't contain an equal sign.
IsAcceptableChar(int key)2204349cc55cSDimitry Andric   bool IsAcceptableChar(int key) override {
2205349cc55cSDimitry Andric     return TextFieldDelegate::IsAcceptableChar(key) && key != '=';
2206349cc55cSDimitry Andric   }
2207349cc55cSDimitry Andric 
GetName()2208349cc55cSDimitry Andric   const std::string &GetName() { return m_content; }
2209349cc55cSDimitry Andric };
2210349cc55cSDimitry Andric 
2211349cc55cSDimitry Andric class EnvironmentVariableFieldDelegate
2212349cc55cSDimitry Andric     : public MappingFieldDelegate<EnvironmentVariableNameFieldDelegate,
2213349cc55cSDimitry Andric                                   TextFieldDelegate> {
2214349cc55cSDimitry Andric public:
EnvironmentVariableFieldDelegate()2215349cc55cSDimitry Andric   EnvironmentVariableFieldDelegate()
2216349cc55cSDimitry Andric       : MappingFieldDelegate(
2217349cc55cSDimitry Andric             EnvironmentVariableNameFieldDelegate(""),
2218349cc55cSDimitry Andric             TextFieldDelegate("Value", "", /*required=*/false)) {}
2219349cc55cSDimitry Andric 
GetName()2220349cc55cSDimitry Andric   const std::string &GetName() { return GetKeyField().GetName(); }
2221349cc55cSDimitry Andric 
GetValue()2222349cc55cSDimitry Andric   const std::string &GetValue() { return GetValueField().GetText(); }
2223349cc55cSDimitry Andric 
SetName(const char * name)2224349cc55cSDimitry Andric   void SetName(const char *name) { return GetKeyField().SetText(name); }
2225349cc55cSDimitry Andric 
SetValue(const char * value)2226349cc55cSDimitry Andric   void SetValue(const char *value) { return GetValueField().SetText(value); }
2227349cc55cSDimitry Andric };
2228349cc55cSDimitry Andric 
2229349cc55cSDimitry Andric class EnvironmentVariableListFieldDelegate
2230349cc55cSDimitry Andric     : public ListFieldDelegate<EnvironmentVariableFieldDelegate> {
2231349cc55cSDimitry Andric public:
EnvironmentVariableListFieldDelegate(const char * label)2232349cc55cSDimitry Andric   EnvironmentVariableListFieldDelegate(const char *label)
2233349cc55cSDimitry Andric       : ListFieldDelegate(label, EnvironmentVariableFieldDelegate()) {}
2234349cc55cSDimitry Andric 
GetEnvironment()2235349cc55cSDimitry Andric   Environment GetEnvironment() {
2236349cc55cSDimitry Andric     Environment environment;
2237349cc55cSDimitry Andric     for (int i = 0; i < GetNumberOfFields(); i++) {
2238349cc55cSDimitry Andric       environment.insert(
2239349cc55cSDimitry Andric           std::make_pair(GetField(i).GetName(), GetField(i).GetValue()));
2240349cc55cSDimitry Andric     }
2241349cc55cSDimitry Andric     return environment;
2242349cc55cSDimitry Andric   }
2243349cc55cSDimitry Andric 
AddEnvironmentVariables(const Environment & environment)2244349cc55cSDimitry Andric   void AddEnvironmentVariables(const Environment &environment) {
2245349cc55cSDimitry Andric     for (auto &variable : environment) {
2246349cc55cSDimitry Andric       AddNewField();
2247349cc55cSDimitry Andric       EnvironmentVariableFieldDelegate &field =
2248349cc55cSDimitry Andric           GetField(GetNumberOfFields() - 1);
2249349cc55cSDimitry Andric       field.SetName(variable.getKey().str().c_str());
2250349cc55cSDimitry Andric       field.SetValue(variable.getValue().c_str());
2251349cc55cSDimitry Andric     }
2252349cc55cSDimitry Andric   }
2253349cc55cSDimitry Andric };
2254349cc55cSDimitry Andric 
2255fe6060f1SDimitry Andric class FormAction {
2256fe6060f1SDimitry Andric public:
FormAction(const char * label,std::function<void (Window &)> action)2257fe6060f1SDimitry Andric   FormAction(const char *label, std::function<void(Window &)> action)
2258fe6060f1SDimitry Andric       : m_action(action) {
2259fe6060f1SDimitry Andric     if (label)
2260fe6060f1SDimitry Andric       m_label = label;
2261fe6060f1SDimitry Andric   }
2262fe6060f1SDimitry Andric 
2263fe6060f1SDimitry Andric   // Draw a centered [Label].
Draw(Surface & surface,bool is_selected)2264349cc55cSDimitry Andric   void Draw(Surface &surface, bool is_selected) {
2265fe6060f1SDimitry Andric     int x = (surface.GetWidth() - m_label.length()) / 2;
2266fe6060f1SDimitry Andric     surface.MoveCursor(x, 0);
2267fe6060f1SDimitry Andric     if (is_selected)
2268fe6060f1SDimitry Andric       surface.AttributeOn(A_REVERSE);
2269fe6060f1SDimitry Andric     surface.PutChar('[');
2270fe6060f1SDimitry Andric     surface.PutCString(m_label.c_str());
2271fe6060f1SDimitry Andric     surface.PutChar(']');
2272fe6060f1SDimitry Andric     if (is_selected)
2273fe6060f1SDimitry Andric       surface.AttributeOff(A_REVERSE);
2274fe6060f1SDimitry Andric   }
2275fe6060f1SDimitry Andric 
Execute(Window & window)2276fe6060f1SDimitry Andric   void Execute(Window &window) { m_action(window); }
2277fe6060f1SDimitry Andric 
GetLabel()2278fe6060f1SDimitry Andric   const std::string &GetLabel() { return m_label; }
2279fe6060f1SDimitry Andric 
2280fe6060f1SDimitry Andric protected:
2281fe6060f1SDimitry Andric   std::string m_label;
2282fe6060f1SDimitry Andric   std::function<void(Window &)> m_action;
2283fe6060f1SDimitry Andric };
2284fe6060f1SDimitry Andric 
2285fe6060f1SDimitry Andric class FormDelegate {
2286fe6060f1SDimitry Andric public:
228781ad6265SDimitry Andric   FormDelegate() = default;
2288fe6060f1SDimitry Andric 
2289fe6060f1SDimitry Andric   virtual ~FormDelegate() = default;
2290fe6060f1SDimitry Andric 
2291fe6060f1SDimitry Andric   virtual std::string GetName() = 0;
2292fe6060f1SDimitry Andric 
UpdateFieldsVisibility()22930eae32dcSDimitry Andric   virtual void UpdateFieldsVisibility() {}
2294fe6060f1SDimitry Andric 
GetField(uint32_t field_index)2295fe6060f1SDimitry Andric   FieldDelegate *GetField(uint32_t field_index) {
2296fe6060f1SDimitry Andric     if (field_index < m_fields.size())
2297fe6060f1SDimitry Andric       return m_fields[field_index].get();
2298fe6060f1SDimitry Andric     return nullptr;
2299fe6060f1SDimitry Andric   }
2300fe6060f1SDimitry Andric 
GetAction(int action_index)2301fe6060f1SDimitry Andric   FormAction &GetAction(int action_index) { return m_actions[action_index]; }
2302fe6060f1SDimitry Andric 
GetNumberOfFields()2303fe6060f1SDimitry Andric   int GetNumberOfFields() { return m_fields.size(); }
2304fe6060f1SDimitry Andric 
GetNumberOfActions()2305fe6060f1SDimitry Andric   int GetNumberOfActions() { return m_actions.size(); }
2306fe6060f1SDimitry Andric 
HasError()2307fe6060f1SDimitry Andric   bool HasError() { return !m_error.empty(); }
2308fe6060f1SDimitry Andric 
ClearError()2309fe6060f1SDimitry Andric   void ClearError() { m_error.clear(); }
2310fe6060f1SDimitry Andric 
GetError()2311fe6060f1SDimitry Andric   const std::string &GetError() { return m_error; }
2312fe6060f1SDimitry Andric 
SetError(const char * error)2313fe6060f1SDimitry Andric   void SetError(const char *error) { m_error = error; }
2314fe6060f1SDimitry Andric 
2315fe6060f1SDimitry Andric   // If all fields are valid, true is returned. Otherwise, an error message is
2316fe6060f1SDimitry Andric   // set and false is returned. This method is usually called at the start of an
2317fe6060f1SDimitry Andric   // action that requires valid fields.
CheckFieldsValidity()2318fe6060f1SDimitry Andric   bool CheckFieldsValidity() {
2319fe6060f1SDimitry Andric     for (int i = 0; i < GetNumberOfFields(); i++) {
2320349cc55cSDimitry Andric       GetField(i)->FieldDelegateExitCallback();
2321fe6060f1SDimitry Andric       if (GetField(i)->FieldDelegateHasError()) {
2322fe6060f1SDimitry Andric         SetError("Some fields are invalid!");
2323fe6060f1SDimitry Andric         return false;
2324fe6060f1SDimitry Andric       }
2325fe6060f1SDimitry Andric     }
2326fe6060f1SDimitry Andric     return true;
2327fe6060f1SDimitry Andric   }
2328fe6060f1SDimitry Andric 
2329fe6060f1SDimitry Andric   // Factory methods to create and add fields of specific types.
2330fe6060f1SDimitry Andric 
AddTextField(const char * label,const char * content,bool required)2331fe6060f1SDimitry Andric   TextFieldDelegate *AddTextField(const char *label, const char *content,
2332fe6060f1SDimitry Andric                                   bool required) {
2333fe6060f1SDimitry Andric     TextFieldDelegate *delegate =
2334fe6060f1SDimitry Andric         new TextFieldDelegate(label, content, required);
2335fe6060f1SDimitry Andric     m_fields.push_back(FieldDelegateUP(delegate));
2336fe6060f1SDimitry Andric     return delegate;
2337fe6060f1SDimitry Andric   }
2338fe6060f1SDimitry Andric 
AddFileField(const char * label,const char * content,bool need_to_exist,bool required)2339fe6060f1SDimitry Andric   FileFieldDelegate *AddFileField(const char *label, const char *content,
2340fe6060f1SDimitry Andric                                   bool need_to_exist, bool required) {
2341fe6060f1SDimitry Andric     FileFieldDelegate *delegate =
2342fe6060f1SDimitry Andric         new FileFieldDelegate(label, content, need_to_exist, required);
2343fe6060f1SDimitry Andric     m_fields.push_back(FieldDelegateUP(delegate));
2344fe6060f1SDimitry Andric     return delegate;
2345fe6060f1SDimitry Andric   }
2346fe6060f1SDimitry Andric 
AddDirectoryField(const char * label,const char * content,bool need_to_exist,bool required)2347fe6060f1SDimitry Andric   DirectoryFieldDelegate *AddDirectoryField(const char *label,
2348fe6060f1SDimitry Andric                                             const char *content,
2349fe6060f1SDimitry Andric                                             bool need_to_exist, bool required) {
2350fe6060f1SDimitry Andric     DirectoryFieldDelegate *delegate =
2351fe6060f1SDimitry Andric         new DirectoryFieldDelegate(label, content, need_to_exist, required);
2352fe6060f1SDimitry Andric     m_fields.push_back(FieldDelegateUP(delegate));
2353fe6060f1SDimitry Andric     return delegate;
2354fe6060f1SDimitry Andric   }
2355fe6060f1SDimitry Andric 
AddArchField(const char * label,const char * content,bool required)2356fe6060f1SDimitry Andric   ArchFieldDelegate *AddArchField(const char *label, const char *content,
2357fe6060f1SDimitry Andric                                   bool required) {
2358fe6060f1SDimitry Andric     ArchFieldDelegate *delegate =
2359fe6060f1SDimitry Andric         new ArchFieldDelegate(label, content, required);
2360fe6060f1SDimitry Andric     m_fields.push_back(FieldDelegateUP(delegate));
2361fe6060f1SDimitry Andric     return delegate;
2362fe6060f1SDimitry Andric   }
2363fe6060f1SDimitry Andric 
AddIntegerField(const char * label,int content,bool required)2364fe6060f1SDimitry Andric   IntegerFieldDelegate *AddIntegerField(const char *label, int content,
2365fe6060f1SDimitry Andric                                         bool required) {
2366fe6060f1SDimitry Andric     IntegerFieldDelegate *delegate =
2367fe6060f1SDimitry Andric         new IntegerFieldDelegate(label, content, required);
2368fe6060f1SDimitry Andric     m_fields.push_back(FieldDelegateUP(delegate));
2369fe6060f1SDimitry Andric     return delegate;
2370fe6060f1SDimitry Andric   }
2371fe6060f1SDimitry Andric 
AddBooleanField(const char * label,bool content)2372fe6060f1SDimitry Andric   BooleanFieldDelegate *AddBooleanField(const char *label, bool content) {
2373fe6060f1SDimitry Andric     BooleanFieldDelegate *delegate = new BooleanFieldDelegate(label, content);
2374fe6060f1SDimitry Andric     m_fields.push_back(FieldDelegateUP(delegate));
2375fe6060f1SDimitry Andric     return delegate;
2376fe6060f1SDimitry Andric   }
2377fe6060f1SDimitry Andric 
AddLazyBooleanField(const char * label,const char * calculate_label)2378349cc55cSDimitry Andric   LazyBooleanFieldDelegate *AddLazyBooleanField(const char *label,
2379349cc55cSDimitry Andric                                                 const char *calculate_label) {
2380349cc55cSDimitry Andric     LazyBooleanFieldDelegate *delegate =
2381349cc55cSDimitry Andric         new LazyBooleanFieldDelegate(label, calculate_label);
2382349cc55cSDimitry Andric     m_fields.push_back(FieldDelegateUP(delegate));
2383349cc55cSDimitry Andric     return delegate;
2384349cc55cSDimitry Andric   }
2385349cc55cSDimitry Andric 
AddChoicesField(const char * label,int height,std::vector<std::string> choices)2386fe6060f1SDimitry Andric   ChoicesFieldDelegate *AddChoicesField(const char *label, int height,
2387fe6060f1SDimitry Andric                                         std::vector<std::string> choices) {
2388fe6060f1SDimitry Andric     ChoicesFieldDelegate *delegate =
2389fe6060f1SDimitry Andric         new ChoicesFieldDelegate(label, height, choices);
2390fe6060f1SDimitry Andric     m_fields.push_back(FieldDelegateUP(delegate));
2391fe6060f1SDimitry Andric     return delegate;
2392fe6060f1SDimitry Andric   }
2393fe6060f1SDimitry Andric 
AddPlatformPluginField(Debugger & debugger)2394fe6060f1SDimitry Andric   PlatformPluginFieldDelegate *AddPlatformPluginField(Debugger &debugger) {
2395fe6060f1SDimitry Andric     PlatformPluginFieldDelegate *delegate =
2396fe6060f1SDimitry Andric         new PlatformPluginFieldDelegate(debugger);
2397fe6060f1SDimitry Andric     m_fields.push_back(FieldDelegateUP(delegate));
2398fe6060f1SDimitry Andric     return delegate;
2399fe6060f1SDimitry Andric   }
2400fe6060f1SDimitry Andric 
AddProcessPluginField()2401fe6060f1SDimitry Andric   ProcessPluginFieldDelegate *AddProcessPluginField() {
2402fe6060f1SDimitry Andric     ProcessPluginFieldDelegate *delegate = new ProcessPluginFieldDelegate();
2403fe6060f1SDimitry Andric     m_fields.push_back(FieldDelegateUP(delegate));
2404fe6060f1SDimitry Andric     return delegate;
2405fe6060f1SDimitry Andric   }
2406fe6060f1SDimitry Andric 
2407fe6060f1SDimitry Andric   template <class T>
AddListField(const char * label,T default_field)2408fe6060f1SDimitry Andric   ListFieldDelegate<T> *AddListField(const char *label, T default_field) {
2409fe6060f1SDimitry Andric     ListFieldDelegate<T> *delegate =
2410fe6060f1SDimitry Andric         new ListFieldDelegate<T>(label, default_field);
2411fe6060f1SDimitry Andric     m_fields.push_back(FieldDelegateUP(delegate));
2412fe6060f1SDimitry Andric     return delegate;
2413fe6060f1SDimitry Andric   }
2414fe6060f1SDimitry Andric 
AddArgumentsField()2415349cc55cSDimitry Andric   ArgumentsFieldDelegate *AddArgumentsField() {
2416349cc55cSDimitry Andric     ArgumentsFieldDelegate *delegate = new ArgumentsFieldDelegate();
2417349cc55cSDimitry Andric     m_fields.push_back(FieldDelegateUP(delegate));
2418349cc55cSDimitry Andric     return delegate;
2419349cc55cSDimitry Andric   }
2420349cc55cSDimitry Andric 
2421349cc55cSDimitry Andric   template <class K, class V>
AddMappingField(K key_field,V value_field)2422349cc55cSDimitry Andric   MappingFieldDelegate<K, V> *AddMappingField(K key_field, V value_field) {
2423349cc55cSDimitry Andric     MappingFieldDelegate<K, V> *delegate =
2424349cc55cSDimitry Andric         new MappingFieldDelegate<K, V>(key_field, value_field);
2425349cc55cSDimitry Andric     m_fields.push_back(FieldDelegateUP(delegate));
2426349cc55cSDimitry Andric     return delegate;
2427349cc55cSDimitry Andric   }
2428349cc55cSDimitry Andric 
2429349cc55cSDimitry Andric   EnvironmentVariableNameFieldDelegate *
AddEnvironmentVariableNameField(const char * content)2430349cc55cSDimitry Andric   AddEnvironmentVariableNameField(const char *content) {
2431349cc55cSDimitry Andric     EnvironmentVariableNameFieldDelegate *delegate =
2432349cc55cSDimitry Andric         new EnvironmentVariableNameFieldDelegate(content);
2433349cc55cSDimitry Andric     m_fields.push_back(FieldDelegateUP(delegate));
2434349cc55cSDimitry Andric     return delegate;
2435349cc55cSDimitry Andric   }
2436349cc55cSDimitry Andric 
AddEnvironmentVariableField()2437349cc55cSDimitry Andric   EnvironmentVariableFieldDelegate *AddEnvironmentVariableField() {
2438349cc55cSDimitry Andric     EnvironmentVariableFieldDelegate *delegate =
2439349cc55cSDimitry Andric         new EnvironmentVariableFieldDelegate();
2440349cc55cSDimitry Andric     m_fields.push_back(FieldDelegateUP(delegate));
2441349cc55cSDimitry Andric     return delegate;
2442349cc55cSDimitry Andric   }
2443349cc55cSDimitry Andric 
2444349cc55cSDimitry Andric   EnvironmentVariableListFieldDelegate *
AddEnvironmentVariableListField(const char * label)2445349cc55cSDimitry Andric   AddEnvironmentVariableListField(const char *label) {
2446349cc55cSDimitry Andric     EnvironmentVariableListFieldDelegate *delegate =
2447349cc55cSDimitry Andric         new EnvironmentVariableListFieldDelegate(label);
2448349cc55cSDimitry Andric     m_fields.push_back(FieldDelegateUP(delegate));
2449349cc55cSDimitry Andric     return delegate;
2450349cc55cSDimitry Andric   }
2451349cc55cSDimitry Andric 
2452fe6060f1SDimitry Andric   // Factory methods for adding actions.
2453fe6060f1SDimitry Andric 
AddAction(const char * label,std::function<void (Window &)> action)2454fe6060f1SDimitry Andric   void AddAction(const char *label, std::function<void(Window &)> action) {
2455fe6060f1SDimitry Andric     m_actions.push_back(FormAction(label, action));
2456fe6060f1SDimitry Andric   }
2457fe6060f1SDimitry Andric 
2458fe6060f1SDimitry Andric protected:
2459fe6060f1SDimitry Andric   std::vector<FieldDelegateUP> m_fields;
2460fe6060f1SDimitry Andric   std::vector<FormAction> m_actions;
2461fe6060f1SDimitry Andric   // Optional error message. If empty, form is considered to have no error.
2462fe6060f1SDimitry Andric   std::string m_error;
2463fe6060f1SDimitry Andric };
2464fe6060f1SDimitry Andric 
2465fe6060f1SDimitry Andric typedef std::shared_ptr<FormDelegate> FormDelegateSP;
2466fe6060f1SDimitry Andric 
2467fe6060f1SDimitry Andric class FormWindowDelegate : public WindowDelegate {
2468fe6060f1SDimitry Andric public:
FormWindowDelegate(FormDelegateSP & delegate_sp)246981ad6265SDimitry Andric   FormWindowDelegate(FormDelegateSP &delegate_sp) : m_delegate_sp(delegate_sp) {
2470fe6060f1SDimitry Andric     assert(m_delegate_sp->GetNumberOfActions() > 0);
2471fe6060f1SDimitry Andric     if (m_delegate_sp->GetNumberOfFields() > 0)
2472fe6060f1SDimitry Andric       m_selection_type = SelectionType::Field;
2473fe6060f1SDimitry Andric     else
2474fe6060f1SDimitry Andric       m_selection_type = SelectionType::Action;
2475fe6060f1SDimitry Andric   }
2476fe6060f1SDimitry Andric 
2477fe6060f1SDimitry Andric   // Signify which element is selected. If a field or an action is selected,
2478fe6060f1SDimitry Andric   // then m_selection_index signifies the particular field or action that is
2479fe6060f1SDimitry Andric   // selected.
2480fe6060f1SDimitry Andric   enum class SelectionType { Field, Action };
2481fe6060f1SDimitry Andric 
2482fe6060f1SDimitry Andric   // A form window is padded by one character from all sides. First, if an error
2483fe6060f1SDimitry Andric   // message exists, it is drawn followed by a separator. Then one or more
2484fe6060f1SDimitry Andric   // fields are drawn. Finally, all available actions are drawn on a single
2485fe6060f1SDimitry Andric   // line.
2486fe6060f1SDimitry Andric   //
2487fe6060f1SDimitry Andric   // ___<Form Name>_________________________________________________
2488fe6060f1SDimitry Andric   // |                                                             |
2489fe6060f1SDimitry Andric   // | - Error message if it exists.                               |
2490fe6060f1SDimitry Andric   // |-------------------------------------------------------------|
2491fe6060f1SDimitry Andric   // | Form elements here.                                         |
2492fe6060f1SDimitry Andric   // |                       Form actions here.                    |
2493fe6060f1SDimitry Andric   // |                                                             |
2494fe6060f1SDimitry Andric   // |______________________________________[Press Esc to cancel]__|
2495fe6060f1SDimitry Andric   //
2496fe6060f1SDimitry Andric 
2497fe6060f1SDimitry Andric   // One line for the error and another for the horizontal line.
GetErrorHeight()2498fe6060f1SDimitry Andric   int GetErrorHeight() {
2499fe6060f1SDimitry Andric     if (m_delegate_sp->HasError())
2500fe6060f1SDimitry Andric       return 2;
2501fe6060f1SDimitry Andric     return 0;
2502fe6060f1SDimitry Andric   }
2503fe6060f1SDimitry Andric 
2504fe6060f1SDimitry Andric   // Actions span a single line.
GetActionsHeight()2505fe6060f1SDimitry Andric   int GetActionsHeight() {
2506fe6060f1SDimitry Andric     if (m_delegate_sp->GetNumberOfActions() > 0)
2507fe6060f1SDimitry Andric       return 1;
2508fe6060f1SDimitry Andric     return 0;
2509fe6060f1SDimitry Andric   }
2510fe6060f1SDimitry Andric 
2511fe6060f1SDimitry Andric   // Get the total number of needed lines to draw the contents.
GetContentHeight()2512fe6060f1SDimitry Andric   int GetContentHeight() {
2513fe6060f1SDimitry Andric     int height = 0;
2514fe6060f1SDimitry Andric     height += GetErrorHeight();
2515fe6060f1SDimitry Andric     for (int i = 0; i < m_delegate_sp->GetNumberOfFields(); i++) {
2516fe6060f1SDimitry Andric       if (!m_delegate_sp->GetField(i)->FieldDelegateIsVisible())
2517fe6060f1SDimitry Andric         continue;
2518fe6060f1SDimitry Andric       height += m_delegate_sp->GetField(i)->FieldDelegateGetHeight();
2519fe6060f1SDimitry Andric     }
2520fe6060f1SDimitry Andric     height += GetActionsHeight();
2521fe6060f1SDimitry Andric     return height;
2522fe6060f1SDimitry Andric   }
2523fe6060f1SDimitry Andric 
GetScrollContext()2524fe6060f1SDimitry Andric   ScrollContext GetScrollContext() {
2525fe6060f1SDimitry Andric     if (m_selection_type == SelectionType::Action)
2526fe6060f1SDimitry Andric       return ScrollContext(GetContentHeight() - 1);
2527fe6060f1SDimitry Andric 
2528fe6060f1SDimitry Andric     FieldDelegate *field = m_delegate_sp->GetField(m_selection_index);
2529fe6060f1SDimitry Andric     ScrollContext context = field->FieldDelegateGetScrollContext();
2530fe6060f1SDimitry Andric 
2531fe6060f1SDimitry Andric     int offset = GetErrorHeight();
2532fe6060f1SDimitry Andric     for (int i = 0; i < m_selection_index; i++) {
2533fe6060f1SDimitry Andric       if (!m_delegate_sp->GetField(i)->FieldDelegateIsVisible())
2534fe6060f1SDimitry Andric         continue;
2535fe6060f1SDimitry Andric       offset += m_delegate_sp->GetField(i)->FieldDelegateGetHeight();
2536fe6060f1SDimitry Andric     }
2537fe6060f1SDimitry Andric     context.Offset(offset);
2538fe6060f1SDimitry Andric 
2539fe6060f1SDimitry Andric     // If the context is touching the error, include the error in the context as
2540fe6060f1SDimitry Andric     // well.
2541fe6060f1SDimitry Andric     if (context.start == GetErrorHeight())
2542fe6060f1SDimitry Andric       context.start = 0;
2543fe6060f1SDimitry Andric 
2544fe6060f1SDimitry Andric     return context;
2545fe6060f1SDimitry Andric   }
2546fe6060f1SDimitry Andric 
UpdateScrolling(Surface & surface)2547349cc55cSDimitry Andric   void UpdateScrolling(Surface &surface) {
2548fe6060f1SDimitry Andric     ScrollContext context = GetScrollContext();
2549fe6060f1SDimitry Andric     int content_height = GetContentHeight();
2550fe6060f1SDimitry Andric     int surface_height = surface.GetHeight();
2551fe6060f1SDimitry Andric     int visible_height = std::min(content_height, surface_height);
2552fe6060f1SDimitry Andric     int last_visible_line = m_first_visible_line + visible_height - 1;
2553fe6060f1SDimitry Andric 
2554fe6060f1SDimitry Andric     // If the last visible line is bigger than the content, then it is invalid
2555fe6060f1SDimitry Andric     // and needs to be set to the last line in the content. This can happen when
2556fe6060f1SDimitry Andric     // a field has shrunk in height.
2557fe6060f1SDimitry Andric     if (last_visible_line > content_height - 1) {
2558fe6060f1SDimitry Andric       m_first_visible_line = content_height - visible_height;
2559fe6060f1SDimitry Andric     }
2560fe6060f1SDimitry Andric 
2561fe6060f1SDimitry Andric     if (context.start < m_first_visible_line) {
2562fe6060f1SDimitry Andric       m_first_visible_line = context.start;
2563fe6060f1SDimitry Andric       return;
2564fe6060f1SDimitry Andric     }
2565fe6060f1SDimitry Andric 
2566fe6060f1SDimitry Andric     if (context.end > last_visible_line) {
2567fe6060f1SDimitry Andric       m_first_visible_line = context.end - visible_height + 1;
2568fe6060f1SDimitry Andric     }
2569fe6060f1SDimitry Andric   }
2570fe6060f1SDimitry Andric 
DrawError(Surface & surface)2571349cc55cSDimitry Andric   void DrawError(Surface &surface) {
2572fe6060f1SDimitry Andric     if (!m_delegate_sp->HasError())
2573fe6060f1SDimitry Andric       return;
2574fe6060f1SDimitry Andric     surface.MoveCursor(0, 0);
2575fe6060f1SDimitry Andric     surface.AttributeOn(COLOR_PAIR(RedOnBlack));
2576fe6060f1SDimitry Andric     surface.PutChar(ACS_DIAMOND);
2577fe6060f1SDimitry Andric     surface.PutChar(' ');
2578fe6060f1SDimitry Andric     surface.PutCStringTruncated(1, m_delegate_sp->GetError().c_str());
2579fe6060f1SDimitry Andric     surface.AttributeOff(COLOR_PAIR(RedOnBlack));
2580fe6060f1SDimitry Andric 
2581fe6060f1SDimitry Andric     surface.MoveCursor(0, 1);
2582fe6060f1SDimitry Andric     surface.HorizontalLine(surface.GetWidth());
2583fe6060f1SDimitry Andric   }
2584fe6060f1SDimitry Andric 
DrawFields(Surface & surface)2585349cc55cSDimitry Andric   void DrawFields(Surface &surface) {
2586fe6060f1SDimitry Andric     int line = 0;
2587fe6060f1SDimitry Andric     int width = surface.GetWidth();
2588fe6060f1SDimitry Andric     bool a_field_is_selected = m_selection_type == SelectionType::Field;
2589fe6060f1SDimitry Andric     for (int i = 0; i < m_delegate_sp->GetNumberOfFields(); i++) {
2590fe6060f1SDimitry Andric       FieldDelegate *field = m_delegate_sp->GetField(i);
2591fe6060f1SDimitry Andric       if (!field->FieldDelegateIsVisible())
2592fe6060f1SDimitry Andric         continue;
2593fe6060f1SDimitry Andric       bool is_field_selected = a_field_is_selected && m_selection_index == i;
2594fe6060f1SDimitry Andric       int height = field->FieldDelegateGetHeight();
2595fe6060f1SDimitry Andric       Rect bounds = Rect(Point(0, line), Size(width, height));
2596349cc55cSDimitry Andric       Surface field_surface = surface.SubSurface(bounds);
2597fe6060f1SDimitry Andric       field->FieldDelegateDraw(field_surface, is_field_selected);
2598fe6060f1SDimitry Andric       line += height;
2599fe6060f1SDimitry Andric     }
2600fe6060f1SDimitry Andric   }
2601fe6060f1SDimitry Andric 
DrawActions(Surface & surface)2602349cc55cSDimitry Andric   void DrawActions(Surface &surface) {
2603fe6060f1SDimitry Andric     int number_of_actions = m_delegate_sp->GetNumberOfActions();
2604fe6060f1SDimitry Andric     int width = surface.GetWidth() / number_of_actions;
2605fe6060f1SDimitry Andric     bool an_action_is_selected = m_selection_type == SelectionType::Action;
2606fe6060f1SDimitry Andric     int x = 0;
2607fe6060f1SDimitry Andric     for (int i = 0; i < number_of_actions; i++) {
2608fe6060f1SDimitry Andric       bool is_action_selected = an_action_is_selected && m_selection_index == i;
2609fe6060f1SDimitry Andric       FormAction &action = m_delegate_sp->GetAction(i);
2610fe6060f1SDimitry Andric       Rect bounds = Rect(Point(x, 0), Size(width, 1));
2611349cc55cSDimitry Andric       Surface action_surface = surface.SubSurface(bounds);
2612fe6060f1SDimitry Andric       action.Draw(action_surface, is_action_selected);
2613fe6060f1SDimitry Andric       x += width;
2614fe6060f1SDimitry Andric     }
2615fe6060f1SDimitry Andric   }
2616fe6060f1SDimitry Andric 
DrawElements(Surface & surface)2617349cc55cSDimitry Andric   void DrawElements(Surface &surface) {
2618fe6060f1SDimitry Andric     Rect frame = surface.GetFrame();
2619fe6060f1SDimitry Andric     Rect fields_bounds, actions_bounds;
2620fe6060f1SDimitry Andric     frame.HorizontalSplit(surface.GetHeight() - GetActionsHeight(),
2621fe6060f1SDimitry Andric                           fields_bounds, actions_bounds);
2622349cc55cSDimitry Andric     Surface fields_surface = surface.SubSurface(fields_bounds);
2623349cc55cSDimitry Andric     Surface actions_surface = surface.SubSurface(actions_bounds);
2624fe6060f1SDimitry Andric 
2625fe6060f1SDimitry Andric     DrawFields(fields_surface);
2626fe6060f1SDimitry Andric     DrawActions(actions_surface);
2627fe6060f1SDimitry Andric   }
2628fe6060f1SDimitry Andric 
2629fe6060f1SDimitry Andric   // Contents are first drawn on a pad. Then a subset of that pad is copied to
2630fe6060f1SDimitry Andric   // the derived window starting at the first visible line. This essentially
2631fe6060f1SDimitry Andric   // provides scrolling functionality.
DrawContent(Surface & surface)2632349cc55cSDimitry Andric   void DrawContent(Surface &surface) {
2633fe6060f1SDimitry Andric     UpdateScrolling(surface);
2634fe6060f1SDimitry Andric 
2635fe6060f1SDimitry Andric     int width = surface.GetWidth();
2636fe6060f1SDimitry Andric     int height = GetContentHeight();
2637fe6060f1SDimitry Andric     Pad pad = Pad(Size(width, height));
2638fe6060f1SDimitry Andric 
2639fe6060f1SDimitry Andric     Rect frame = pad.GetFrame();
2640fe6060f1SDimitry Andric     Rect error_bounds, elements_bounds;
2641fe6060f1SDimitry Andric     frame.HorizontalSplit(GetErrorHeight(), error_bounds, elements_bounds);
2642349cc55cSDimitry Andric     Surface error_surface = pad.SubSurface(error_bounds);
2643349cc55cSDimitry Andric     Surface elements_surface = pad.SubSurface(elements_bounds);
2644fe6060f1SDimitry Andric 
2645fe6060f1SDimitry Andric     DrawError(error_surface);
2646fe6060f1SDimitry Andric     DrawElements(elements_surface);
2647fe6060f1SDimitry Andric 
2648fe6060f1SDimitry Andric     int copy_height = std::min(surface.GetHeight(), pad.GetHeight());
2649fe6060f1SDimitry Andric     pad.CopyToSurface(surface, Point(0, m_first_visible_line), Point(),
2650fe6060f1SDimitry Andric                       Size(width, copy_height));
2651fe6060f1SDimitry Andric   }
2652fe6060f1SDimitry Andric 
DrawSubmitHint(Surface & surface,bool is_active)2653349cc55cSDimitry Andric   void DrawSubmitHint(Surface &surface, bool is_active) {
2654349cc55cSDimitry Andric     surface.MoveCursor(2, surface.GetHeight() - 1);
2655349cc55cSDimitry Andric     if (is_active)
2656349cc55cSDimitry Andric       surface.AttributeOn(A_BOLD | COLOR_PAIR(BlackOnWhite));
2657349cc55cSDimitry Andric     surface.Printf("[Press Alt+Enter to %s]",
2658349cc55cSDimitry Andric                    m_delegate_sp->GetAction(0).GetLabel().c_str());
2659349cc55cSDimitry Andric     if (is_active)
2660349cc55cSDimitry Andric       surface.AttributeOff(A_BOLD | COLOR_PAIR(BlackOnWhite));
2661349cc55cSDimitry Andric   }
2662349cc55cSDimitry Andric 
WindowDelegateDraw(Window & window,bool force)2663fe6060f1SDimitry Andric   bool WindowDelegateDraw(Window &window, bool force) override {
2664fe6060f1SDimitry Andric     m_delegate_sp->UpdateFieldsVisibility();
2665fe6060f1SDimitry Andric 
2666fe6060f1SDimitry Andric     window.Erase();
2667fe6060f1SDimitry Andric 
2668fe6060f1SDimitry Andric     window.DrawTitleBox(m_delegate_sp->GetName().c_str(),
2669349cc55cSDimitry Andric                         "Press Esc to Cancel");
2670349cc55cSDimitry Andric     DrawSubmitHint(window, window.IsActive());
2671fe6060f1SDimitry Andric 
2672fe6060f1SDimitry Andric     Rect content_bounds = window.GetFrame();
2673fe6060f1SDimitry Andric     content_bounds.Inset(2, 2);
2674349cc55cSDimitry Andric     Surface content_surface = window.SubSurface(content_bounds);
2675fe6060f1SDimitry Andric 
2676fe6060f1SDimitry Andric     DrawContent(content_surface);
2677fe6060f1SDimitry Andric     return true;
2678fe6060f1SDimitry Andric   }
2679fe6060f1SDimitry Andric 
SkipNextHiddenFields()2680fe6060f1SDimitry Andric   void SkipNextHiddenFields() {
2681fe6060f1SDimitry Andric     while (true) {
2682fe6060f1SDimitry Andric       if (m_delegate_sp->GetField(m_selection_index)->FieldDelegateIsVisible())
2683fe6060f1SDimitry Andric         return;
2684fe6060f1SDimitry Andric 
2685fe6060f1SDimitry Andric       if (m_selection_index == m_delegate_sp->GetNumberOfFields() - 1) {
2686fe6060f1SDimitry Andric         m_selection_type = SelectionType::Action;
2687fe6060f1SDimitry Andric         m_selection_index = 0;
2688fe6060f1SDimitry Andric         return;
2689fe6060f1SDimitry Andric       }
2690fe6060f1SDimitry Andric 
2691fe6060f1SDimitry Andric       m_selection_index++;
2692fe6060f1SDimitry Andric     }
2693fe6060f1SDimitry Andric   }
2694fe6060f1SDimitry Andric 
SelectNext(int key)2695fe6060f1SDimitry Andric   HandleCharResult SelectNext(int key) {
2696fe6060f1SDimitry Andric     if (m_selection_type == SelectionType::Action) {
2697fe6060f1SDimitry Andric       if (m_selection_index < m_delegate_sp->GetNumberOfActions() - 1) {
2698fe6060f1SDimitry Andric         m_selection_index++;
2699fe6060f1SDimitry Andric         return eKeyHandled;
2700fe6060f1SDimitry Andric       }
2701fe6060f1SDimitry Andric 
2702fe6060f1SDimitry Andric       m_selection_index = 0;
2703fe6060f1SDimitry Andric       m_selection_type = SelectionType::Field;
2704fe6060f1SDimitry Andric       SkipNextHiddenFields();
2705fe6060f1SDimitry Andric       if (m_selection_type == SelectionType::Field) {
2706fe6060f1SDimitry Andric         FieldDelegate *next_field = m_delegate_sp->GetField(m_selection_index);
2707fe6060f1SDimitry Andric         next_field->FieldDelegateSelectFirstElement();
2708fe6060f1SDimitry Andric       }
2709fe6060f1SDimitry Andric       return eKeyHandled;
2710fe6060f1SDimitry Andric     }
2711fe6060f1SDimitry Andric 
2712fe6060f1SDimitry Andric     FieldDelegate *field = m_delegate_sp->GetField(m_selection_index);
2713fe6060f1SDimitry Andric     if (!field->FieldDelegateOnLastOrOnlyElement()) {
2714fe6060f1SDimitry Andric       return field->FieldDelegateHandleChar(key);
2715fe6060f1SDimitry Andric     }
2716fe6060f1SDimitry Andric 
2717fe6060f1SDimitry Andric     field->FieldDelegateExitCallback();
2718fe6060f1SDimitry Andric 
2719fe6060f1SDimitry Andric     if (m_selection_index == m_delegate_sp->GetNumberOfFields() - 1) {
2720fe6060f1SDimitry Andric       m_selection_type = SelectionType::Action;
2721fe6060f1SDimitry Andric       m_selection_index = 0;
2722fe6060f1SDimitry Andric       return eKeyHandled;
2723fe6060f1SDimitry Andric     }
2724fe6060f1SDimitry Andric 
2725fe6060f1SDimitry Andric     m_selection_index++;
2726fe6060f1SDimitry Andric     SkipNextHiddenFields();
2727fe6060f1SDimitry Andric 
2728fe6060f1SDimitry Andric     if (m_selection_type == SelectionType::Field) {
2729fe6060f1SDimitry Andric       FieldDelegate *next_field = m_delegate_sp->GetField(m_selection_index);
2730fe6060f1SDimitry Andric       next_field->FieldDelegateSelectFirstElement();
2731fe6060f1SDimitry Andric     }
2732fe6060f1SDimitry Andric 
2733fe6060f1SDimitry Andric     return eKeyHandled;
2734fe6060f1SDimitry Andric   }
2735fe6060f1SDimitry Andric 
SkipPreviousHiddenFields()2736fe6060f1SDimitry Andric   void SkipPreviousHiddenFields() {
2737fe6060f1SDimitry Andric     while (true) {
2738fe6060f1SDimitry Andric       if (m_delegate_sp->GetField(m_selection_index)->FieldDelegateIsVisible())
2739fe6060f1SDimitry Andric         return;
2740fe6060f1SDimitry Andric 
2741fe6060f1SDimitry Andric       if (m_selection_index == 0) {
2742fe6060f1SDimitry Andric         m_selection_type = SelectionType::Action;
2743fe6060f1SDimitry Andric         m_selection_index = 0;
2744fe6060f1SDimitry Andric         return;
2745fe6060f1SDimitry Andric       }
2746fe6060f1SDimitry Andric 
2747fe6060f1SDimitry Andric       m_selection_index--;
2748fe6060f1SDimitry Andric     }
2749fe6060f1SDimitry Andric   }
2750fe6060f1SDimitry Andric 
SelectPrevious(int key)2751fe6060f1SDimitry Andric   HandleCharResult SelectPrevious(int key) {
2752fe6060f1SDimitry Andric     if (m_selection_type == SelectionType::Action) {
2753fe6060f1SDimitry Andric       if (m_selection_index > 0) {
2754fe6060f1SDimitry Andric         m_selection_index--;
2755fe6060f1SDimitry Andric         return eKeyHandled;
2756fe6060f1SDimitry Andric       }
2757fe6060f1SDimitry Andric       m_selection_index = m_delegate_sp->GetNumberOfFields() - 1;
2758fe6060f1SDimitry Andric       m_selection_type = SelectionType::Field;
2759fe6060f1SDimitry Andric       SkipPreviousHiddenFields();
2760fe6060f1SDimitry Andric       if (m_selection_type == SelectionType::Field) {
2761fe6060f1SDimitry Andric         FieldDelegate *previous_field =
2762fe6060f1SDimitry Andric             m_delegate_sp->GetField(m_selection_index);
2763fe6060f1SDimitry Andric         previous_field->FieldDelegateSelectLastElement();
2764fe6060f1SDimitry Andric       }
2765fe6060f1SDimitry Andric       return eKeyHandled;
2766fe6060f1SDimitry Andric     }
2767fe6060f1SDimitry Andric 
2768fe6060f1SDimitry Andric     FieldDelegate *field = m_delegate_sp->GetField(m_selection_index);
2769fe6060f1SDimitry Andric     if (!field->FieldDelegateOnFirstOrOnlyElement()) {
2770fe6060f1SDimitry Andric       return field->FieldDelegateHandleChar(key);
2771fe6060f1SDimitry Andric     }
2772fe6060f1SDimitry Andric 
2773fe6060f1SDimitry Andric     field->FieldDelegateExitCallback();
2774fe6060f1SDimitry Andric 
2775fe6060f1SDimitry Andric     if (m_selection_index == 0) {
2776fe6060f1SDimitry Andric       m_selection_type = SelectionType::Action;
2777fe6060f1SDimitry Andric       m_selection_index = m_delegate_sp->GetNumberOfActions() - 1;
2778fe6060f1SDimitry Andric       return eKeyHandled;
2779fe6060f1SDimitry Andric     }
2780fe6060f1SDimitry Andric 
2781fe6060f1SDimitry Andric     m_selection_index--;
2782fe6060f1SDimitry Andric     SkipPreviousHiddenFields();
2783fe6060f1SDimitry Andric 
2784fe6060f1SDimitry Andric     if (m_selection_type == SelectionType::Field) {
2785fe6060f1SDimitry Andric       FieldDelegate *previous_field =
2786fe6060f1SDimitry Andric           m_delegate_sp->GetField(m_selection_index);
2787fe6060f1SDimitry Andric       previous_field->FieldDelegateSelectLastElement();
2788fe6060f1SDimitry Andric     }
2789fe6060f1SDimitry Andric 
2790fe6060f1SDimitry Andric     return eKeyHandled;
2791fe6060f1SDimitry Andric   }
2792fe6060f1SDimitry Andric 
ExecuteAction(Window & window,int index)2793349cc55cSDimitry Andric   void ExecuteAction(Window &window, int index) {
2794349cc55cSDimitry Andric     FormAction &action = m_delegate_sp->GetAction(index);
2795fe6060f1SDimitry Andric     action.Execute(window);
2796fe6060f1SDimitry Andric     if (m_delegate_sp->HasError()) {
2797fe6060f1SDimitry Andric       m_first_visible_line = 0;
2798fe6060f1SDimitry Andric       m_selection_index = 0;
2799fe6060f1SDimitry Andric       m_selection_type = SelectionType::Field;
2800fe6060f1SDimitry Andric     }
2801fe6060f1SDimitry Andric   }
2802fe6060f1SDimitry Andric 
2803349cc55cSDimitry Andric   // Always return eKeyHandled to absorb all events since forms are always
2804349cc55cSDimitry Andric   // added as pop-ups that should take full control until canceled or submitted.
WindowDelegateHandleChar(Window & window,int key)2805fe6060f1SDimitry Andric   HandleCharResult WindowDelegateHandleChar(Window &window, int key) override {
2806fe6060f1SDimitry Andric     switch (key) {
2807fe6060f1SDimitry Andric     case '\r':
2808fe6060f1SDimitry Andric     case '\n':
2809fe6060f1SDimitry Andric     case KEY_ENTER:
2810fe6060f1SDimitry Andric       if (m_selection_type == SelectionType::Action) {
2811349cc55cSDimitry Andric         ExecuteAction(window, m_selection_index);
2812fe6060f1SDimitry Andric         return eKeyHandled;
2813fe6060f1SDimitry Andric       }
2814fe6060f1SDimitry Andric       break;
2815349cc55cSDimitry Andric     case KEY_ALT_ENTER:
2816349cc55cSDimitry Andric       ExecuteAction(window, 0);
2817349cc55cSDimitry Andric       return eKeyHandled;
2818fe6060f1SDimitry Andric     case '\t':
2819349cc55cSDimitry Andric       SelectNext(key);
2820349cc55cSDimitry Andric       return eKeyHandled;
2821fe6060f1SDimitry Andric     case KEY_SHIFT_TAB:
2822349cc55cSDimitry Andric       SelectPrevious(key);
2823349cc55cSDimitry Andric       return eKeyHandled;
2824fe6060f1SDimitry Andric     case KEY_ESCAPE:
2825fe6060f1SDimitry Andric       window.GetParent()->RemoveSubWindow(&window);
2826fe6060f1SDimitry Andric       return eKeyHandled;
2827fe6060f1SDimitry Andric     default:
2828fe6060f1SDimitry Andric       break;
2829fe6060f1SDimitry Andric     }
2830fe6060f1SDimitry Andric 
2831fe6060f1SDimitry Andric     // If the key wasn't handled and one of the fields is selected, pass the key
2832fe6060f1SDimitry Andric     // to that field.
2833fe6060f1SDimitry Andric     if (m_selection_type == SelectionType::Field) {
2834fe6060f1SDimitry Andric       FieldDelegate *field = m_delegate_sp->GetField(m_selection_index);
2835349cc55cSDimitry Andric       if (field->FieldDelegateHandleChar(key) == eKeyHandled)
2836349cc55cSDimitry Andric         return eKeyHandled;
2837fe6060f1SDimitry Andric     }
2838fe6060f1SDimitry Andric 
2839349cc55cSDimitry Andric     // If the key wasn't handled by the possibly selected field, handle some
2840349cc55cSDimitry Andric     // extra keys for navigation.
2841349cc55cSDimitry Andric     switch (key) {
2842349cc55cSDimitry Andric     case KEY_DOWN:
2843349cc55cSDimitry Andric       SelectNext(key);
2844349cc55cSDimitry Andric       return eKeyHandled;
2845349cc55cSDimitry Andric     case KEY_UP:
2846349cc55cSDimitry Andric       SelectPrevious(key);
2847349cc55cSDimitry Andric       return eKeyHandled;
2848349cc55cSDimitry Andric     default:
2849349cc55cSDimitry Andric       break;
2850349cc55cSDimitry Andric     }
2851349cc55cSDimitry Andric 
2852349cc55cSDimitry Andric     return eKeyHandled;
2853fe6060f1SDimitry Andric   }
2854fe6060f1SDimitry Andric 
2855fe6060f1SDimitry Andric protected:
2856fe6060f1SDimitry Andric   FormDelegateSP m_delegate_sp;
2857fe6060f1SDimitry Andric   // The index of the currently selected SelectionType.
285881ad6265SDimitry Andric   int m_selection_index = 0;
2859fe6060f1SDimitry Andric   // See SelectionType class enum.
2860fe6060f1SDimitry Andric   SelectionType m_selection_type;
2861fe6060f1SDimitry Andric   // The first visible line from the pad.
286281ad6265SDimitry Andric   int m_first_visible_line = 0;
2863fe6060f1SDimitry Andric };
2864fe6060f1SDimitry Andric 
2865fe6060f1SDimitry Andric ///////////////////////////
2866fe6060f1SDimitry Andric // Form Delegate Instances
2867fe6060f1SDimitry Andric ///////////////////////////
2868fe6060f1SDimitry Andric 
2869fe6060f1SDimitry Andric class DetachOrKillProcessFormDelegate : public FormDelegate {
2870fe6060f1SDimitry Andric public:
DetachOrKillProcessFormDelegate(Process * process)2871fe6060f1SDimitry Andric   DetachOrKillProcessFormDelegate(Process *process) : m_process(process) {
2872fe6060f1SDimitry Andric     SetError("There is a running process, either detach or kill it.");
2873fe6060f1SDimitry Andric 
2874fe6060f1SDimitry Andric     m_keep_stopped_field =
2875fe6060f1SDimitry Andric         AddBooleanField("Keep process stopped when detaching.", false);
2876fe6060f1SDimitry Andric 
2877fe6060f1SDimitry Andric     AddAction("Detach", [this](Window &window) { Detach(window); });
2878fe6060f1SDimitry Andric     AddAction("Kill", [this](Window &window) { Kill(window); });
2879fe6060f1SDimitry Andric   }
2880fe6060f1SDimitry Andric 
GetName()2881fe6060f1SDimitry Andric   std::string GetName() override { return "Detach/Kill Process"; }
2882fe6060f1SDimitry Andric 
Kill(Window & window)2883fe6060f1SDimitry Andric   void Kill(Window &window) {
2884fe6060f1SDimitry Andric     Status destroy_status(m_process->Destroy(false));
2885fe6060f1SDimitry Andric     if (destroy_status.Fail()) {
2886fe6060f1SDimitry Andric       SetError("Failed to kill process.");
2887fe6060f1SDimitry Andric       return;
2888fe6060f1SDimitry Andric     }
2889fe6060f1SDimitry Andric     window.GetParent()->RemoveSubWindow(&window);
2890fe6060f1SDimitry Andric   }
2891fe6060f1SDimitry Andric 
Detach(Window & window)2892fe6060f1SDimitry Andric   void Detach(Window &window) {
2893fe6060f1SDimitry Andric     Status detach_status(m_process->Detach(m_keep_stopped_field->GetBoolean()));
2894fe6060f1SDimitry Andric     if (detach_status.Fail()) {
2895fe6060f1SDimitry Andric       SetError("Failed to detach from process.");
2896fe6060f1SDimitry Andric       return;
2897fe6060f1SDimitry Andric     }
2898fe6060f1SDimitry Andric     window.GetParent()->RemoveSubWindow(&window);
2899fe6060f1SDimitry Andric   }
2900fe6060f1SDimitry Andric 
2901fe6060f1SDimitry Andric protected:
2902fe6060f1SDimitry Andric   Process *m_process;
2903fe6060f1SDimitry Andric   BooleanFieldDelegate *m_keep_stopped_field;
2904fe6060f1SDimitry Andric };
2905fe6060f1SDimitry Andric 
2906fe6060f1SDimitry Andric class ProcessAttachFormDelegate : public FormDelegate {
2907fe6060f1SDimitry Andric public:
ProcessAttachFormDelegate(Debugger & debugger,WindowSP main_window_sp)2908fe6060f1SDimitry Andric   ProcessAttachFormDelegate(Debugger &debugger, WindowSP main_window_sp)
2909fe6060f1SDimitry Andric       : m_debugger(debugger), m_main_window_sp(main_window_sp) {
2910fe6060f1SDimitry Andric     std::vector<std::string> types;
2911fe6060f1SDimitry Andric     types.push_back(std::string("Name"));
2912fe6060f1SDimitry Andric     types.push_back(std::string("PID"));
2913fe6060f1SDimitry Andric     m_type_field = AddChoicesField("Attach By", 2, types);
2914fe6060f1SDimitry Andric     m_pid_field = AddIntegerField("PID", 0, true);
2915fe6060f1SDimitry Andric     m_name_field =
2916fe6060f1SDimitry Andric         AddTextField("Process Name", GetDefaultProcessName().c_str(), true);
2917fe6060f1SDimitry Andric     m_continue_field = AddBooleanField("Continue once attached.", false);
2918fe6060f1SDimitry Andric     m_wait_for_field = AddBooleanField("Wait for process to launch.", false);
2919fe6060f1SDimitry Andric     m_include_existing_field =
2920fe6060f1SDimitry Andric         AddBooleanField("Include existing processes.", false);
2921fe6060f1SDimitry Andric     m_show_advanced_field = AddBooleanField("Show advanced settings.", false);
2922fe6060f1SDimitry Andric     m_plugin_field = AddProcessPluginField();
2923fe6060f1SDimitry Andric 
2924fe6060f1SDimitry Andric     AddAction("Attach", [this](Window &window) { Attach(window); });
2925fe6060f1SDimitry Andric   }
2926fe6060f1SDimitry Andric 
GetName()2927fe6060f1SDimitry Andric   std::string GetName() override { return "Attach Process"; }
2928fe6060f1SDimitry Andric 
UpdateFieldsVisibility()2929fe6060f1SDimitry Andric   void UpdateFieldsVisibility() override {
2930fe6060f1SDimitry Andric     if (m_type_field->GetChoiceContent() == "Name") {
2931fe6060f1SDimitry Andric       m_pid_field->FieldDelegateHide();
2932fe6060f1SDimitry Andric       m_name_field->FieldDelegateShow();
2933fe6060f1SDimitry Andric       m_wait_for_field->FieldDelegateShow();
2934fe6060f1SDimitry Andric       if (m_wait_for_field->GetBoolean())
2935fe6060f1SDimitry Andric         m_include_existing_field->FieldDelegateShow();
2936fe6060f1SDimitry Andric       else
2937fe6060f1SDimitry Andric         m_include_existing_field->FieldDelegateHide();
2938fe6060f1SDimitry Andric     } else {
2939fe6060f1SDimitry Andric       m_pid_field->FieldDelegateShow();
2940fe6060f1SDimitry Andric       m_name_field->FieldDelegateHide();
2941fe6060f1SDimitry Andric       m_wait_for_field->FieldDelegateHide();
2942fe6060f1SDimitry Andric       m_include_existing_field->FieldDelegateHide();
2943fe6060f1SDimitry Andric     }
2944fe6060f1SDimitry Andric     if (m_show_advanced_field->GetBoolean())
2945fe6060f1SDimitry Andric       m_plugin_field->FieldDelegateShow();
2946fe6060f1SDimitry Andric     else
2947fe6060f1SDimitry Andric       m_plugin_field->FieldDelegateHide();
2948fe6060f1SDimitry Andric   }
2949fe6060f1SDimitry Andric 
2950fe6060f1SDimitry Andric   // Get the basename of the target's main executable if available, empty string
2951fe6060f1SDimitry Andric   // otherwise.
GetDefaultProcessName()2952fe6060f1SDimitry Andric   std::string GetDefaultProcessName() {
2953fe6060f1SDimitry Andric     Target *target = m_debugger.GetSelectedTarget().get();
2954fe6060f1SDimitry Andric     if (target == nullptr)
2955fe6060f1SDimitry Andric       return "";
2956fe6060f1SDimitry Andric 
2957fe6060f1SDimitry Andric     ModuleSP module_sp = target->GetExecutableModule();
2958fe6060f1SDimitry Andric     if (!module_sp->IsExecutable())
2959fe6060f1SDimitry Andric       return "";
2960fe6060f1SDimitry Andric 
2961fe6060f1SDimitry Andric     return module_sp->GetFileSpec().GetFilename().AsCString();
2962fe6060f1SDimitry Andric   }
2963fe6060f1SDimitry Andric 
StopRunningProcess()2964fe6060f1SDimitry Andric   bool StopRunningProcess() {
2965fe6060f1SDimitry Andric     ExecutionContext exe_ctx =
2966fe6060f1SDimitry Andric         m_debugger.GetCommandInterpreter().GetExecutionContext();
2967fe6060f1SDimitry Andric 
2968fe6060f1SDimitry Andric     if (!exe_ctx.HasProcessScope())
2969fe6060f1SDimitry Andric       return false;
2970fe6060f1SDimitry Andric 
2971fe6060f1SDimitry Andric     Process *process = exe_ctx.GetProcessPtr();
2972fe6060f1SDimitry Andric     if (!(process && process->IsAlive()))
2973fe6060f1SDimitry Andric       return false;
2974fe6060f1SDimitry Andric 
2975fe6060f1SDimitry Andric     FormDelegateSP form_delegate_sp =
2976fe6060f1SDimitry Andric         FormDelegateSP(new DetachOrKillProcessFormDelegate(process));
2977fe6060f1SDimitry Andric     Rect bounds = m_main_window_sp->GetCenteredRect(85, 8);
2978fe6060f1SDimitry Andric     WindowSP form_window_sp = m_main_window_sp->CreateSubWindow(
2979fe6060f1SDimitry Andric         form_delegate_sp->GetName().c_str(), bounds, true);
2980fe6060f1SDimitry Andric     WindowDelegateSP window_delegate_sp =
2981fe6060f1SDimitry Andric         WindowDelegateSP(new FormWindowDelegate(form_delegate_sp));
2982fe6060f1SDimitry Andric     form_window_sp->SetDelegate(window_delegate_sp);
2983fe6060f1SDimitry Andric 
2984fe6060f1SDimitry Andric     return true;
2985fe6060f1SDimitry Andric   }
2986fe6060f1SDimitry Andric 
GetTarget()2987fe6060f1SDimitry Andric   Target *GetTarget() {
2988fe6060f1SDimitry Andric     Target *target = m_debugger.GetSelectedTarget().get();
2989fe6060f1SDimitry Andric 
2990fe6060f1SDimitry Andric     if (target != nullptr)
2991fe6060f1SDimitry Andric       return target;
2992fe6060f1SDimitry Andric 
2993fe6060f1SDimitry Andric     TargetSP new_target_sp;
2994fe6060f1SDimitry Andric     m_debugger.GetTargetList().CreateTarget(
2995fe6060f1SDimitry Andric         m_debugger, "", "", eLoadDependentsNo, nullptr, new_target_sp);
2996fe6060f1SDimitry Andric 
2997fe6060f1SDimitry Andric     target = new_target_sp.get();
2998fe6060f1SDimitry Andric 
2999fe6060f1SDimitry Andric     if (target == nullptr)
3000fe6060f1SDimitry Andric       SetError("Failed to create target.");
3001fe6060f1SDimitry Andric 
3002fe6060f1SDimitry Andric     m_debugger.GetTargetList().SetSelectedTarget(new_target_sp);
3003fe6060f1SDimitry Andric 
3004fe6060f1SDimitry Andric     return target;
3005fe6060f1SDimitry Andric   }
3006fe6060f1SDimitry Andric 
GetAttachInfo()3007fe6060f1SDimitry Andric   ProcessAttachInfo GetAttachInfo() {
3008fe6060f1SDimitry Andric     ProcessAttachInfo attach_info;
3009fe6060f1SDimitry Andric     attach_info.SetContinueOnceAttached(m_continue_field->GetBoolean());
3010fe6060f1SDimitry Andric     if (m_type_field->GetChoiceContent() == "Name") {
3011fe6060f1SDimitry Andric       attach_info.GetExecutableFile().SetFile(m_name_field->GetText(),
3012fe6060f1SDimitry Andric                                               FileSpec::Style::native);
3013fe6060f1SDimitry Andric       attach_info.SetWaitForLaunch(m_wait_for_field->GetBoolean());
3014fe6060f1SDimitry Andric       if (m_wait_for_field->GetBoolean())
3015fe6060f1SDimitry Andric         attach_info.SetIgnoreExisting(!m_include_existing_field->GetBoolean());
3016fe6060f1SDimitry Andric     } else {
3017fe6060f1SDimitry Andric       attach_info.SetProcessID(m_pid_field->GetInteger());
3018fe6060f1SDimitry Andric     }
3019fe6060f1SDimitry Andric     attach_info.SetProcessPluginName(m_plugin_field->GetPluginName());
3020fe6060f1SDimitry Andric 
3021fe6060f1SDimitry Andric     return attach_info;
3022fe6060f1SDimitry Andric   }
3023fe6060f1SDimitry Andric 
Attach(Window & window)3024fe6060f1SDimitry Andric   void Attach(Window &window) {
3025fe6060f1SDimitry Andric     ClearError();
3026fe6060f1SDimitry Andric 
3027fe6060f1SDimitry Andric     bool all_fields_are_valid = CheckFieldsValidity();
3028fe6060f1SDimitry Andric     if (!all_fields_are_valid)
3029fe6060f1SDimitry Andric       return;
3030fe6060f1SDimitry Andric 
3031fe6060f1SDimitry Andric     bool process_is_running = StopRunningProcess();
3032fe6060f1SDimitry Andric     if (process_is_running)
3033fe6060f1SDimitry Andric       return;
3034fe6060f1SDimitry Andric 
3035fe6060f1SDimitry Andric     Target *target = GetTarget();
3036fe6060f1SDimitry Andric     if (HasError())
3037fe6060f1SDimitry Andric       return;
3038fe6060f1SDimitry Andric 
3039fe6060f1SDimitry Andric     StreamString stream;
3040fe6060f1SDimitry Andric     ProcessAttachInfo attach_info = GetAttachInfo();
3041fe6060f1SDimitry Andric     Status status = target->Attach(attach_info, &stream);
3042fe6060f1SDimitry Andric 
3043fe6060f1SDimitry Andric     if (status.Fail()) {
3044fe6060f1SDimitry Andric       SetError(status.AsCString());
3045fe6060f1SDimitry Andric       return;
3046fe6060f1SDimitry Andric     }
3047fe6060f1SDimitry Andric 
3048fe6060f1SDimitry Andric     ProcessSP process_sp(target->GetProcessSP());
3049fe6060f1SDimitry Andric     if (!process_sp) {
3050fe6060f1SDimitry Andric       SetError("Attached sucessfully but target has no process.");
3051fe6060f1SDimitry Andric       return;
3052fe6060f1SDimitry Andric     }
3053fe6060f1SDimitry Andric 
3054fe6060f1SDimitry Andric     if (attach_info.GetContinueOnceAttached())
3055fe6060f1SDimitry Andric       process_sp->Resume();
3056fe6060f1SDimitry Andric 
3057fe6060f1SDimitry Andric     window.GetParent()->RemoveSubWindow(&window);
3058fe6060f1SDimitry Andric   }
3059fe6060f1SDimitry Andric 
3060fe6060f1SDimitry Andric protected:
3061fe6060f1SDimitry Andric   Debugger &m_debugger;
3062fe6060f1SDimitry Andric   WindowSP m_main_window_sp;
3063fe6060f1SDimitry Andric 
3064fe6060f1SDimitry Andric   ChoicesFieldDelegate *m_type_field;
3065fe6060f1SDimitry Andric   IntegerFieldDelegate *m_pid_field;
3066fe6060f1SDimitry Andric   TextFieldDelegate *m_name_field;
3067fe6060f1SDimitry Andric   BooleanFieldDelegate *m_continue_field;
3068fe6060f1SDimitry Andric   BooleanFieldDelegate *m_wait_for_field;
3069fe6060f1SDimitry Andric   BooleanFieldDelegate *m_include_existing_field;
3070fe6060f1SDimitry Andric   BooleanFieldDelegate *m_show_advanced_field;
3071fe6060f1SDimitry Andric   ProcessPluginFieldDelegate *m_plugin_field;
3072fe6060f1SDimitry Andric };
3073fe6060f1SDimitry Andric 
3074349cc55cSDimitry Andric class TargetCreateFormDelegate : public FormDelegate {
3075349cc55cSDimitry Andric public:
TargetCreateFormDelegate(Debugger & debugger)3076349cc55cSDimitry Andric   TargetCreateFormDelegate(Debugger &debugger) : m_debugger(debugger) {
3077349cc55cSDimitry Andric     m_executable_field = AddFileField("Executable", "", /*need_to_exist=*/true,
3078349cc55cSDimitry Andric                                       /*required=*/true);
3079349cc55cSDimitry Andric     m_core_file_field = AddFileField("Core File", "", /*need_to_exist=*/true,
3080349cc55cSDimitry Andric                                      /*required=*/false);
3081349cc55cSDimitry Andric     m_symbol_file_field = AddFileField(
3082349cc55cSDimitry Andric         "Symbol File", "", /*need_to_exist=*/true, /*required=*/false);
3083349cc55cSDimitry Andric     m_show_advanced_field = AddBooleanField("Show advanced settings.", false);
3084349cc55cSDimitry Andric     m_remote_file_field = AddFileField(
3085349cc55cSDimitry Andric         "Remote File", "", /*need_to_exist=*/false, /*required=*/false);
3086349cc55cSDimitry Andric     m_arch_field = AddArchField("Architecture", "", /*required=*/false);
3087349cc55cSDimitry Andric     m_platform_field = AddPlatformPluginField(debugger);
3088349cc55cSDimitry Andric     m_load_dependent_files_field =
3089349cc55cSDimitry Andric         AddChoicesField("Load Dependents", 3, GetLoadDependentFilesChoices());
3090349cc55cSDimitry Andric 
3091349cc55cSDimitry Andric     AddAction("Create", [this](Window &window) { CreateTarget(window); });
3092349cc55cSDimitry Andric   }
3093349cc55cSDimitry Andric 
GetName()3094349cc55cSDimitry Andric   std::string GetName() override { return "Create Target"; }
3095349cc55cSDimitry Andric 
UpdateFieldsVisibility()3096349cc55cSDimitry Andric   void UpdateFieldsVisibility() override {
3097349cc55cSDimitry Andric     if (m_show_advanced_field->GetBoolean()) {
3098349cc55cSDimitry Andric       m_remote_file_field->FieldDelegateShow();
3099349cc55cSDimitry Andric       m_arch_field->FieldDelegateShow();
3100349cc55cSDimitry Andric       m_platform_field->FieldDelegateShow();
3101349cc55cSDimitry Andric       m_load_dependent_files_field->FieldDelegateShow();
3102349cc55cSDimitry Andric     } else {
3103349cc55cSDimitry Andric       m_remote_file_field->FieldDelegateHide();
3104349cc55cSDimitry Andric       m_arch_field->FieldDelegateHide();
3105349cc55cSDimitry Andric       m_platform_field->FieldDelegateHide();
3106349cc55cSDimitry Andric       m_load_dependent_files_field->FieldDelegateHide();
3107349cc55cSDimitry Andric     }
3108349cc55cSDimitry Andric   }
3109349cc55cSDimitry Andric 
3110349cc55cSDimitry Andric   static constexpr const char *kLoadDependentFilesNo = "No";
3111349cc55cSDimitry Andric   static constexpr const char *kLoadDependentFilesYes = "Yes";
3112349cc55cSDimitry Andric   static constexpr const char *kLoadDependentFilesExecOnly = "Executable only";
3113349cc55cSDimitry Andric 
GetLoadDependentFilesChoices()3114349cc55cSDimitry Andric   std::vector<std::string> GetLoadDependentFilesChoices() {
3115bdd1243dSDimitry Andric     std::vector<std::string> load_dependents_options;
3116bdd1243dSDimitry Andric     load_dependents_options.push_back(kLoadDependentFilesExecOnly);
3117bdd1243dSDimitry Andric     load_dependents_options.push_back(kLoadDependentFilesYes);
3118bdd1243dSDimitry Andric     load_dependents_options.push_back(kLoadDependentFilesNo);
3119bdd1243dSDimitry Andric     return load_dependents_options;
3120349cc55cSDimitry Andric   }
3121349cc55cSDimitry Andric 
GetLoadDependentFiles()3122349cc55cSDimitry Andric   LoadDependentFiles GetLoadDependentFiles() {
3123349cc55cSDimitry Andric     std::string choice = m_load_dependent_files_field->GetChoiceContent();
3124349cc55cSDimitry Andric     if (choice == kLoadDependentFilesNo)
3125349cc55cSDimitry Andric       return eLoadDependentsNo;
3126349cc55cSDimitry Andric     if (choice == kLoadDependentFilesYes)
3127349cc55cSDimitry Andric       return eLoadDependentsYes;
3128349cc55cSDimitry Andric     return eLoadDependentsDefault;
3129349cc55cSDimitry Andric   }
3130349cc55cSDimitry Andric 
GetPlatformOptions()3131349cc55cSDimitry Andric   OptionGroupPlatform GetPlatformOptions() {
3132349cc55cSDimitry Andric     OptionGroupPlatform platform_options(false);
3133349cc55cSDimitry Andric     platform_options.SetPlatformName(m_platform_field->GetPluginName().c_str());
3134349cc55cSDimitry Andric     return platform_options;
3135349cc55cSDimitry Andric   }
3136349cc55cSDimitry Andric 
GetTarget()3137349cc55cSDimitry Andric   TargetSP GetTarget() {
3138349cc55cSDimitry Andric     OptionGroupPlatform platform_options = GetPlatformOptions();
3139349cc55cSDimitry Andric     TargetSP target_sp;
3140349cc55cSDimitry Andric     Status status = m_debugger.GetTargetList().CreateTarget(
3141349cc55cSDimitry Andric         m_debugger, m_executable_field->GetPath(),
3142349cc55cSDimitry Andric         m_arch_field->GetArchString(), GetLoadDependentFiles(),
3143349cc55cSDimitry Andric         &platform_options, target_sp);
3144349cc55cSDimitry Andric 
3145349cc55cSDimitry Andric     if (status.Fail()) {
3146349cc55cSDimitry Andric       SetError(status.AsCString());
3147349cc55cSDimitry Andric       return nullptr;
3148349cc55cSDimitry Andric     }
3149349cc55cSDimitry Andric 
3150349cc55cSDimitry Andric     m_debugger.GetTargetList().SetSelectedTarget(target_sp);
3151349cc55cSDimitry Andric 
3152349cc55cSDimitry Andric     return target_sp;
3153349cc55cSDimitry Andric   }
3154349cc55cSDimitry Andric 
SetSymbolFile(TargetSP target_sp)3155349cc55cSDimitry Andric   void SetSymbolFile(TargetSP target_sp) {
3156349cc55cSDimitry Andric     if (!m_symbol_file_field->IsSpecified())
3157349cc55cSDimitry Andric       return;
3158349cc55cSDimitry Andric 
3159349cc55cSDimitry Andric     ModuleSP module_sp(target_sp->GetExecutableModule());
3160349cc55cSDimitry Andric     if (!module_sp)
3161349cc55cSDimitry Andric       return;
3162349cc55cSDimitry Andric 
3163349cc55cSDimitry Andric     module_sp->SetSymbolFileFileSpec(
3164349cc55cSDimitry Andric         m_symbol_file_field->GetResolvedFileSpec());
3165349cc55cSDimitry Andric   }
3166349cc55cSDimitry Andric 
SetCoreFile(TargetSP target_sp)3167349cc55cSDimitry Andric   void SetCoreFile(TargetSP target_sp) {
3168349cc55cSDimitry Andric     if (!m_core_file_field->IsSpecified())
3169349cc55cSDimitry Andric       return;
3170349cc55cSDimitry Andric 
3171349cc55cSDimitry Andric     FileSpec core_file_spec = m_core_file_field->GetResolvedFileSpec();
3172349cc55cSDimitry Andric 
3173349cc55cSDimitry Andric     FileSpec core_file_directory_spec;
3174bdd1243dSDimitry Andric     core_file_directory_spec.SetDirectory(core_file_spec.GetDirectory());
3175349cc55cSDimitry Andric     target_sp->AppendExecutableSearchPaths(core_file_directory_spec);
3176349cc55cSDimitry Andric 
3177349cc55cSDimitry Andric     ProcessSP process_sp(target_sp->CreateProcess(
3178349cc55cSDimitry Andric         m_debugger.GetListener(), llvm::StringRef(), &core_file_spec, false));
3179349cc55cSDimitry Andric 
3180349cc55cSDimitry Andric     if (!process_sp) {
3181*5f757f3fSDimitry Andric       SetError("Unknown core file format!");
3182349cc55cSDimitry Andric       return;
3183349cc55cSDimitry Andric     }
3184349cc55cSDimitry Andric 
3185349cc55cSDimitry Andric     Status status = process_sp->LoadCore();
3186349cc55cSDimitry Andric     if (status.Fail()) {
3187*5f757f3fSDimitry Andric       SetError("Unknown core file format!");
3188349cc55cSDimitry Andric       return;
3189349cc55cSDimitry Andric     }
3190349cc55cSDimitry Andric   }
3191349cc55cSDimitry Andric 
SetRemoteFile(TargetSP target_sp)3192349cc55cSDimitry Andric   void SetRemoteFile(TargetSP target_sp) {
3193349cc55cSDimitry Andric     if (!m_remote_file_field->IsSpecified())
3194349cc55cSDimitry Andric       return;
3195349cc55cSDimitry Andric 
3196349cc55cSDimitry Andric     ModuleSP module_sp(target_sp->GetExecutableModule());
3197349cc55cSDimitry Andric     if (!module_sp)
3198349cc55cSDimitry Andric       return;
3199349cc55cSDimitry Andric 
3200349cc55cSDimitry Andric     FileSpec remote_file_spec = m_remote_file_field->GetFileSpec();
3201349cc55cSDimitry Andric     module_sp->SetPlatformFileSpec(remote_file_spec);
3202349cc55cSDimitry Andric   }
3203349cc55cSDimitry Andric 
RemoveTarget(TargetSP target_sp)3204349cc55cSDimitry Andric   void RemoveTarget(TargetSP target_sp) {
3205349cc55cSDimitry Andric     m_debugger.GetTargetList().DeleteTarget(target_sp);
3206349cc55cSDimitry Andric   }
3207349cc55cSDimitry Andric 
CreateTarget(Window & window)3208349cc55cSDimitry Andric   void CreateTarget(Window &window) {
3209349cc55cSDimitry Andric     ClearError();
3210349cc55cSDimitry Andric 
3211349cc55cSDimitry Andric     bool all_fields_are_valid = CheckFieldsValidity();
3212349cc55cSDimitry Andric     if (!all_fields_are_valid)
3213349cc55cSDimitry Andric       return;
3214349cc55cSDimitry Andric 
3215349cc55cSDimitry Andric     TargetSP target_sp = GetTarget();
3216349cc55cSDimitry Andric     if (HasError())
3217349cc55cSDimitry Andric       return;
3218349cc55cSDimitry Andric 
3219349cc55cSDimitry Andric     SetSymbolFile(target_sp);
3220349cc55cSDimitry Andric     if (HasError()) {
3221349cc55cSDimitry Andric       RemoveTarget(target_sp);
3222349cc55cSDimitry Andric       return;
3223349cc55cSDimitry Andric     }
3224349cc55cSDimitry Andric 
3225349cc55cSDimitry Andric     SetCoreFile(target_sp);
3226349cc55cSDimitry Andric     if (HasError()) {
3227349cc55cSDimitry Andric       RemoveTarget(target_sp);
3228349cc55cSDimitry Andric       return;
3229349cc55cSDimitry Andric     }
3230349cc55cSDimitry Andric 
3231349cc55cSDimitry Andric     SetRemoteFile(target_sp);
3232349cc55cSDimitry Andric     if (HasError()) {
3233349cc55cSDimitry Andric       RemoveTarget(target_sp);
3234349cc55cSDimitry Andric       return;
3235349cc55cSDimitry Andric     }
3236349cc55cSDimitry Andric 
3237349cc55cSDimitry Andric     window.GetParent()->RemoveSubWindow(&window);
3238349cc55cSDimitry Andric   }
3239349cc55cSDimitry Andric 
3240349cc55cSDimitry Andric protected:
3241349cc55cSDimitry Andric   Debugger &m_debugger;
3242349cc55cSDimitry Andric 
3243349cc55cSDimitry Andric   FileFieldDelegate *m_executable_field;
3244349cc55cSDimitry Andric   FileFieldDelegate *m_core_file_field;
3245349cc55cSDimitry Andric   FileFieldDelegate *m_symbol_file_field;
3246349cc55cSDimitry Andric   BooleanFieldDelegate *m_show_advanced_field;
3247349cc55cSDimitry Andric   FileFieldDelegate *m_remote_file_field;
3248349cc55cSDimitry Andric   ArchFieldDelegate *m_arch_field;
3249349cc55cSDimitry Andric   PlatformPluginFieldDelegate *m_platform_field;
3250349cc55cSDimitry Andric   ChoicesFieldDelegate *m_load_dependent_files_field;
3251349cc55cSDimitry Andric };
3252349cc55cSDimitry Andric 
3253349cc55cSDimitry Andric class ProcessLaunchFormDelegate : public FormDelegate {
3254349cc55cSDimitry Andric public:
ProcessLaunchFormDelegate(Debugger & debugger,WindowSP main_window_sp)3255349cc55cSDimitry Andric   ProcessLaunchFormDelegate(Debugger &debugger, WindowSP main_window_sp)
3256349cc55cSDimitry Andric       : m_debugger(debugger), m_main_window_sp(main_window_sp) {
3257349cc55cSDimitry Andric 
3258349cc55cSDimitry Andric     m_arguments_field = AddArgumentsField();
3259349cc55cSDimitry Andric     SetArgumentsFieldDefaultValue();
3260349cc55cSDimitry Andric     m_target_environment_field =
3261349cc55cSDimitry Andric         AddEnvironmentVariableListField("Target Environment Variables");
3262349cc55cSDimitry Andric     SetTargetEnvironmentFieldDefaultValue();
3263349cc55cSDimitry Andric     m_working_directory_field = AddDirectoryField(
3264349cc55cSDimitry Andric         "Working Directory", GetDefaultWorkingDirectory().c_str(), true, false);
3265349cc55cSDimitry Andric 
3266349cc55cSDimitry Andric     m_show_advanced_field = AddBooleanField("Show advanced settings.", false);
3267349cc55cSDimitry Andric 
3268349cc55cSDimitry Andric     m_stop_at_entry_field = AddBooleanField("Stop at entry point.", false);
3269349cc55cSDimitry Andric     m_detach_on_error_field =
3270349cc55cSDimitry Andric         AddBooleanField("Detach on error.", GetDefaultDetachOnError());
3271349cc55cSDimitry Andric     m_disable_aslr_field =
3272349cc55cSDimitry Andric         AddBooleanField("Disable ASLR", GetDefaultDisableASLR());
3273349cc55cSDimitry Andric     m_plugin_field = AddProcessPluginField();
3274349cc55cSDimitry Andric     m_arch_field = AddArchField("Architecture", "", false);
3275349cc55cSDimitry Andric     m_shell_field = AddFileField("Shell", "", true, false);
3276349cc55cSDimitry Andric     m_expand_shell_arguments_field =
3277349cc55cSDimitry Andric         AddBooleanField("Expand shell arguments.", false);
3278349cc55cSDimitry Andric 
3279349cc55cSDimitry Andric     m_disable_standard_io_field =
3280349cc55cSDimitry Andric         AddBooleanField("Disable Standard IO", GetDefaultDisableStandardIO());
3281349cc55cSDimitry Andric     m_standard_output_field =
3282349cc55cSDimitry Andric         AddFileField("Standard Output File", "", /*need_to_exist=*/false,
3283349cc55cSDimitry Andric                      /*required=*/false);
3284349cc55cSDimitry Andric     m_standard_error_field =
3285349cc55cSDimitry Andric         AddFileField("Standard Error File", "", /*need_to_exist=*/false,
3286349cc55cSDimitry Andric                      /*required=*/false);
3287349cc55cSDimitry Andric     m_standard_input_field =
3288349cc55cSDimitry Andric         AddFileField("Standard Input File", "", /*need_to_exist=*/false,
3289349cc55cSDimitry Andric                      /*required=*/false);
3290349cc55cSDimitry Andric 
3291349cc55cSDimitry Andric     m_show_inherited_environment_field =
3292349cc55cSDimitry Andric         AddBooleanField("Show inherited environment variables.", false);
3293349cc55cSDimitry Andric     m_inherited_environment_field =
3294349cc55cSDimitry Andric         AddEnvironmentVariableListField("Inherited Environment Variables");
3295349cc55cSDimitry Andric     SetInheritedEnvironmentFieldDefaultValue();
3296349cc55cSDimitry Andric 
3297349cc55cSDimitry Andric     AddAction("Launch", [this](Window &window) { Launch(window); });
3298349cc55cSDimitry Andric   }
3299349cc55cSDimitry Andric 
GetName()3300349cc55cSDimitry Andric   std::string GetName() override { return "Launch Process"; }
3301349cc55cSDimitry Andric 
UpdateFieldsVisibility()3302349cc55cSDimitry Andric   void UpdateFieldsVisibility() override {
3303349cc55cSDimitry Andric     if (m_show_advanced_field->GetBoolean()) {
3304349cc55cSDimitry Andric       m_stop_at_entry_field->FieldDelegateShow();
3305349cc55cSDimitry Andric       m_detach_on_error_field->FieldDelegateShow();
3306349cc55cSDimitry Andric       m_disable_aslr_field->FieldDelegateShow();
3307349cc55cSDimitry Andric       m_plugin_field->FieldDelegateShow();
3308349cc55cSDimitry Andric       m_arch_field->FieldDelegateShow();
3309349cc55cSDimitry Andric       m_shell_field->FieldDelegateShow();
3310349cc55cSDimitry Andric       m_expand_shell_arguments_field->FieldDelegateShow();
3311349cc55cSDimitry Andric       m_disable_standard_io_field->FieldDelegateShow();
3312349cc55cSDimitry Andric       if (m_disable_standard_io_field->GetBoolean()) {
3313349cc55cSDimitry Andric         m_standard_input_field->FieldDelegateHide();
3314349cc55cSDimitry Andric         m_standard_output_field->FieldDelegateHide();
3315349cc55cSDimitry Andric         m_standard_error_field->FieldDelegateHide();
3316349cc55cSDimitry Andric       } else {
3317349cc55cSDimitry Andric         m_standard_input_field->FieldDelegateShow();
3318349cc55cSDimitry Andric         m_standard_output_field->FieldDelegateShow();
3319349cc55cSDimitry Andric         m_standard_error_field->FieldDelegateShow();
3320349cc55cSDimitry Andric       }
3321349cc55cSDimitry Andric       m_show_inherited_environment_field->FieldDelegateShow();
3322349cc55cSDimitry Andric       if (m_show_inherited_environment_field->GetBoolean())
3323349cc55cSDimitry Andric         m_inherited_environment_field->FieldDelegateShow();
3324349cc55cSDimitry Andric       else
3325349cc55cSDimitry Andric         m_inherited_environment_field->FieldDelegateHide();
3326349cc55cSDimitry Andric     } else {
3327349cc55cSDimitry Andric       m_stop_at_entry_field->FieldDelegateHide();
3328349cc55cSDimitry Andric       m_detach_on_error_field->FieldDelegateHide();
3329349cc55cSDimitry Andric       m_disable_aslr_field->FieldDelegateHide();
3330349cc55cSDimitry Andric       m_plugin_field->FieldDelegateHide();
3331349cc55cSDimitry Andric       m_arch_field->FieldDelegateHide();
3332349cc55cSDimitry Andric       m_shell_field->FieldDelegateHide();
3333349cc55cSDimitry Andric       m_expand_shell_arguments_field->FieldDelegateHide();
3334349cc55cSDimitry Andric       m_disable_standard_io_field->FieldDelegateHide();
3335349cc55cSDimitry Andric       m_standard_input_field->FieldDelegateHide();
3336349cc55cSDimitry Andric       m_standard_output_field->FieldDelegateHide();
3337349cc55cSDimitry Andric       m_standard_error_field->FieldDelegateHide();
3338349cc55cSDimitry Andric       m_show_inherited_environment_field->FieldDelegateHide();
3339349cc55cSDimitry Andric       m_inherited_environment_field->FieldDelegateHide();
3340349cc55cSDimitry Andric     }
3341349cc55cSDimitry Andric   }
3342349cc55cSDimitry Andric 
3343349cc55cSDimitry Andric   // Methods for setting the default value of the fields.
3344349cc55cSDimitry Andric 
SetArgumentsFieldDefaultValue()3345349cc55cSDimitry Andric   void SetArgumentsFieldDefaultValue() {
3346349cc55cSDimitry Andric     TargetSP target = m_debugger.GetSelectedTarget();
3347349cc55cSDimitry Andric     if (target == nullptr)
3348349cc55cSDimitry Andric       return;
3349349cc55cSDimitry Andric 
3350349cc55cSDimitry Andric     const Args &target_arguments =
3351349cc55cSDimitry Andric         target->GetProcessLaunchInfo().GetArguments();
3352349cc55cSDimitry Andric     m_arguments_field->AddArguments(target_arguments);
3353349cc55cSDimitry Andric   }
3354349cc55cSDimitry Andric 
SetTargetEnvironmentFieldDefaultValue()3355349cc55cSDimitry Andric   void SetTargetEnvironmentFieldDefaultValue() {
3356349cc55cSDimitry Andric     TargetSP target = m_debugger.GetSelectedTarget();
3357349cc55cSDimitry Andric     if (target == nullptr)
3358349cc55cSDimitry Andric       return;
3359349cc55cSDimitry Andric 
3360349cc55cSDimitry Andric     const Environment &target_environment = target->GetTargetEnvironment();
3361349cc55cSDimitry Andric     m_target_environment_field->AddEnvironmentVariables(target_environment);
3362349cc55cSDimitry Andric   }
3363349cc55cSDimitry Andric 
SetInheritedEnvironmentFieldDefaultValue()3364349cc55cSDimitry Andric   void SetInheritedEnvironmentFieldDefaultValue() {
3365349cc55cSDimitry Andric     TargetSP target = m_debugger.GetSelectedTarget();
3366349cc55cSDimitry Andric     if (target == nullptr)
3367349cc55cSDimitry Andric       return;
3368349cc55cSDimitry Andric 
3369349cc55cSDimitry Andric     const Environment &inherited_environment =
3370349cc55cSDimitry Andric         target->GetInheritedEnvironment();
3371349cc55cSDimitry Andric     m_inherited_environment_field->AddEnvironmentVariables(
3372349cc55cSDimitry Andric         inherited_environment);
3373349cc55cSDimitry Andric   }
3374349cc55cSDimitry Andric 
GetDefaultWorkingDirectory()3375349cc55cSDimitry Andric   std::string GetDefaultWorkingDirectory() {
3376349cc55cSDimitry Andric     TargetSP target = m_debugger.GetSelectedTarget();
3377349cc55cSDimitry Andric     if (target == nullptr)
3378349cc55cSDimitry Andric       return "";
3379349cc55cSDimitry Andric 
3380349cc55cSDimitry Andric     PlatformSP platform = target->GetPlatform();
3381349cc55cSDimitry Andric     return platform->GetWorkingDirectory().GetPath();
3382349cc55cSDimitry Andric   }
3383349cc55cSDimitry Andric 
GetDefaultDisableASLR()3384349cc55cSDimitry Andric   bool GetDefaultDisableASLR() {
3385349cc55cSDimitry Andric     TargetSP target = m_debugger.GetSelectedTarget();
3386349cc55cSDimitry Andric     if (target == nullptr)
3387349cc55cSDimitry Andric       return false;
3388349cc55cSDimitry Andric 
3389349cc55cSDimitry Andric     return target->GetDisableASLR();
3390349cc55cSDimitry Andric   }
3391349cc55cSDimitry Andric 
GetDefaultDisableStandardIO()3392349cc55cSDimitry Andric   bool GetDefaultDisableStandardIO() {
3393349cc55cSDimitry Andric     TargetSP target = m_debugger.GetSelectedTarget();
3394349cc55cSDimitry Andric     if (target == nullptr)
3395349cc55cSDimitry Andric       return true;
3396349cc55cSDimitry Andric 
3397349cc55cSDimitry Andric     return target->GetDisableSTDIO();
3398349cc55cSDimitry Andric   }
3399349cc55cSDimitry Andric 
GetDefaultDetachOnError()3400349cc55cSDimitry Andric   bool GetDefaultDetachOnError() {
3401349cc55cSDimitry Andric     TargetSP target = m_debugger.GetSelectedTarget();
3402349cc55cSDimitry Andric     if (target == nullptr)
3403349cc55cSDimitry Andric       return true;
3404349cc55cSDimitry Andric 
3405349cc55cSDimitry Andric     return target->GetDetachOnError();
3406349cc55cSDimitry Andric   }
3407349cc55cSDimitry Andric 
3408349cc55cSDimitry Andric   // Methods for getting the necessary information and setting them to the
3409349cc55cSDimitry Andric   // ProcessLaunchInfo.
3410349cc55cSDimitry Andric 
GetExecutableSettings(ProcessLaunchInfo & launch_info)3411349cc55cSDimitry Andric   void GetExecutableSettings(ProcessLaunchInfo &launch_info) {
3412349cc55cSDimitry Andric     TargetSP target = m_debugger.GetSelectedTarget();
3413349cc55cSDimitry Andric     ModuleSP executable_module = target->GetExecutableModule();
3414349cc55cSDimitry Andric     llvm::StringRef target_settings_argv0 = target->GetArg0();
3415349cc55cSDimitry Andric 
3416349cc55cSDimitry Andric     if (!target_settings_argv0.empty()) {
3417349cc55cSDimitry Andric       launch_info.GetArguments().AppendArgument(target_settings_argv0);
3418349cc55cSDimitry Andric       launch_info.SetExecutableFile(executable_module->GetPlatformFileSpec(),
3419349cc55cSDimitry Andric                                     false);
3420349cc55cSDimitry Andric       return;
3421349cc55cSDimitry Andric     }
3422349cc55cSDimitry Andric 
3423349cc55cSDimitry Andric     launch_info.SetExecutableFile(executable_module->GetPlatformFileSpec(),
3424349cc55cSDimitry Andric                                   true);
3425349cc55cSDimitry Andric   }
3426349cc55cSDimitry Andric 
GetArguments(ProcessLaunchInfo & launch_info)3427349cc55cSDimitry Andric   void GetArguments(ProcessLaunchInfo &launch_info) {
3428349cc55cSDimitry Andric     TargetSP target = m_debugger.GetSelectedTarget();
3429349cc55cSDimitry Andric     Args arguments = m_arguments_field->GetArguments();
3430349cc55cSDimitry Andric     launch_info.GetArguments().AppendArguments(arguments);
3431349cc55cSDimitry Andric   }
3432349cc55cSDimitry Andric 
GetEnvironment(ProcessLaunchInfo & launch_info)3433349cc55cSDimitry Andric   void GetEnvironment(ProcessLaunchInfo &launch_info) {
3434349cc55cSDimitry Andric     Environment target_environment =
3435349cc55cSDimitry Andric         m_target_environment_field->GetEnvironment();
3436349cc55cSDimitry Andric     Environment inherited_environment =
3437349cc55cSDimitry Andric         m_inherited_environment_field->GetEnvironment();
3438349cc55cSDimitry Andric     launch_info.GetEnvironment().insert(target_environment.begin(),
3439349cc55cSDimitry Andric                                         target_environment.end());
3440349cc55cSDimitry Andric     launch_info.GetEnvironment().insert(inherited_environment.begin(),
3441349cc55cSDimitry Andric                                         inherited_environment.end());
3442349cc55cSDimitry Andric   }
3443349cc55cSDimitry Andric 
GetWorkingDirectory(ProcessLaunchInfo & launch_info)3444349cc55cSDimitry Andric   void GetWorkingDirectory(ProcessLaunchInfo &launch_info) {
3445349cc55cSDimitry Andric     if (m_working_directory_field->IsSpecified())
3446349cc55cSDimitry Andric       launch_info.SetWorkingDirectory(
3447349cc55cSDimitry Andric           m_working_directory_field->GetResolvedFileSpec());
3448349cc55cSDimitry Andric   }
3449349cc55cSDimitry Andric 
GetStopAtEntry(ProcessLaunchInfo & launch_info)3450349cc55cSDimitry Andric   void GetStopAtEntry(ProcessLaunchInfo &launch_info) {
3451349cc55cSDimitry Andric     if (m_stop_at_entry_field->GetBoolean())
3452349cc55cSDimitry Andric       launch_info.GetFlags().Set(eLaunchFlagStopAtEntry);
3453349cc55cSDimitry Andric     else
3454349cc55cSDimitry Andric       launch_info.GetFlags().Clear(eLaunchFlagStopAtEntry);
3455349cc55cSDimitry Andric   }
3456349cc55cSDimitry Andric 
GetDetachOnError(ProcessLaunchInfo & launch_info)3457349cc55cSDimitry Andric   void GetDetachOnError(ProcessLaunchInfo &launch_info) {
3458349cc55cSDimitry Andric     if (m_detach_on_error_field->GetBoolean())
3459349cc55cSDimitry Andric       launch_info.GetFlags().Set(eLaunchFlagDetachOnError);
3460349cc55cSDimitry Andric     else
3461349cc55cSDimitry Andric       launch_info.GetFlags().Clear(eLaunchFlagDetachOnError);
3462349cc55cSDimitry Andric   }
3463349cc55cSDimitry Andric 
GetDisableASLR(ProcessLaunchInfo & launch_info)3464349cc55cSDimitry Andric   void GetDisableASLR(ProcessLaunchInfo &launch_info) {
3465349cc55cSDimitry Andric     if (m_disable_aslr_field->GetBoolean())
3466349cc55cSDimitry Andric       launch_info.GetFlags().Set(eLaunchFlagDisableASLR);
3467349cc55cSDimitry Andric     else
3468349cc55cSDimitry Andric       launch_info.GetFlags().Clear(eLaunchFlagDisableASLR);
3469349cc55cSDimitry Andric   }
3470349cc55cSDimitry Andric 
GetPlugin(ProcessLaunchInfo & launch_info)3471349cc55cSDimitry Andric   void GetPlugin(ProcessLaunchInfo &launch_info) {
3472349cc55cSDimitry Andric     launch_info.SetProcessPluginName(m_plugin_field->GetPluginName());
3473349cc55cSDimitry Andric   }
3474349cc55cSDimitry Andric 
GetArch(ProcessLaunchInfo & launch_info)3475349cc55cSDimitry Andric   void GetArch(ProcessLaunchInfo &launch_info) {
3476349cc55cSDimitry Andric     if (!m_arch_field->IsSpecified())
3477349cc55cSDimitry Andric       return;
3478349cc55cSDimitry Andric 
3479349cc55cSDimitry Andric     TargetSP target_sp = m_debugger.GetSelectedTarget();
3480349cc55cSDimitry Andric     PlatformSP platform_sp =
3481349cc55cSDimitry Andric         target_sp ? target_sp->GetPlatform() : PlatformSP();
3482349cc55cSDimitry Andric     launch_info.GetArchitecture() = Platform::GetAugmentedArchSpec(
3483349cc55cSDimitry Andric         platform_sp.get(), m_arch_field->GetArchString());
3484349cc55cSDimitry Andric   }
3485349cc55cSDimitry Andric 
GetShell(ProcessLaunchInfo & launch_info)3486349cc55cSDimitry Andric   void GetShell(ProcessLaunchInfo &launch_info) {
3487349cc55cSDimitry Andric     if (!m_shell_field->IsSpecified())
3488349cc55cSDimitry Andric       return;
3489349cc55cSDimitry Andric 
3490349cc55cSDimitry Andric     launch_info.SetShell(m_shell_field->GetResolvedFileSpec());
3491349cc55cSDimitry Andric     launch_info.SetShellExpandArguments(
3492349cc55cSDimitry Andric         m_expand_shell_arguments_field->GetBoolean());
3493349cc55cSDimitry Andric   }
3494349cc55cSDimitry Andric 
GetStandardIO(ProcessLaunchInfo & launch_info)3495349cc55cSDimitry Andric   void GetStandardIO(ProcessLaunchInfo &launch_info) {
3496349cc55cSDimitry Andric     if (m_disable_standard_io_field->GetBoolean()) {
3497349cc55cSDimitry Andric       launch_info.GetFlags().Set(eLaunchFlagDisableSTDIO);
3498349cc55cSDimitry Andric       return;
3499349cc55cSDimitry Andric     }
3500349cc55cSDimitry Andric 
3501349cc55cSDimitry Andric     FileAction action;
3502349cc55cSDimitry Andric     if (m_standard_input_field->IsSpecified()) {
3503bdd1243dSDimitry Andric       if (action.Open(STDIN_FILENO, m_standard_input_field->GetFileSpec(), true,
3504bdd1243dSDimitry Andric                       false))
3505349cc55cSDimitry Andric         launch_info.AppendFileAction(action);
3506349cc55cSDimitry Andric     }
3507349cc55cSDimitry Andric     if (m_standard_output_field->IsSpecified()) {
3508bdd1243dSDimitry Andric       if (action.Open(STDOUT_FILENO, m_standard_output_field->GetFileSpec(),
3509bdd1243dSDimitry Andric                       false, true))
3510349cc55cSDimitry Andric         launch_info.AppendFileAction(action);
3511349cc55cSDimitry Andric     }
3512349cc55cSDimitry Andric     if (m_standard_error_field->IsSpecified()) {
3513bdd1243dSDimitry Andric       if (action.Open(STDERR_FILENO, m_standard_error_field->GetFileSpec(),
3514bdd1243dSDimitry Andric                       false, true))
3515349cc55cSDimitry Andric         launch_info.AppendFileAction(action);
3516349cc55cSDimitry Andric     }
3517349cc55cSDimitry Andric   }
3518349cc55cSDimitry Andric 
GetInheritTCC(ProcessLaunchInfo & launch_info)3519349cc55cSDimitry Andric   void GetInheritTCC(ProcessLaunchInfo &launch_info) {
3520349cc55cSDimitry Andric     if (m_debugger.GetSelectedTarget()->GetInheritTCC())
3521349cc55cSDimitry Andric       launch_info.GetFlags().Set(eLaunchFlagInheritTCCFromParent);
3522349cc55cSDimitry Andric   }
3523349cc55cSDimitry Andric 
GetLaunchInfo()3524349cc55cSDimitry Andric   ProcessLaunchInfo GetLaunchInfo() {
3525349cc55cSDimitry Andric     ProcessLaunchInfo launch_info;
3526349cc55cSDimitry Andric 
3527349cc55cSDimitry Andric     GetExecutableSettings(launch_info);
3528349cc55cSDimitry Andric     GetArguments(launch_info);
3529349cc55cSDimitry Andric     GetEnvironment(launch_info);
3530349cc55cSDimitry Andric     GetWorkingDirectory(launch_info);
3531349cc55cSDimitry Andric     GetStopAtEntry(launch_info);
3532349cc55cSDimitry Andric     GetDetachOnError(launch_info);
3533349cc55cSDimitry Andric     GetDisableASLR(launch_info);
3534349cc55cSDimitry Andric     GetPlugin(launch_info);
3535349cc55cSDimitry Andric     GetArch(launch_info);
3536349cc55cSDimitry Andric     GetShell(launch_info);
3537349cc55cSDimitry Andric     GetStandardIO(launch_info);
3538349cc55cSDimitry Andric     GetInheritTCC(launch_info);
3539349cc55cSDimitry Andric 
3540349cc55cSDimitry Andric     return launch_info;
3541349cc55cSDimitry Andric   }
3542349cc55cSDimitry Andric 
StopRunningProcess()3543349cc55cSDimitry Andric   bool StopRunningProcess() {
3544349cc55cSDimitry Andric     ExecutionContext exe_ctx =
3545349cc55cSDimitry Andric         m_debugger.GetCommandInterpreter().GetExecutionContext();
3546349cc55cSDimitry Andric 
3547349cc55cSDimitry Andric     if (!exe_ctx.HasProcessScope())
3548349cc55cSDimitry Andric       return false;
3549349cc55cSDimitry Andric 
3550349cc55cSDimitry Andric     Process *process = exe_ctx.GetProcessPtr();
3551349cc55cSDimitry Andric     if (!(process && process->IsAlive()))
3552349cc55cSDimitry Andric       return false;
3553349cc55cSDimitry Andric 
3554349cc55cSDimitry Andric     FormDelegateSP form_delegate_sp =
3555349cc55cSDimitry Andric         FormDelegateSP(new DetachOrKillProcessFormDelegate(process));
3556349cc55cSDimitry Andric     Rect bounds = m_main_window_sp->GetCenteredRect(85, 8);
3557349cc55cSDimitry Andric     WindowSP form_window_sp = m_main_window_sp->CreateSubWindow(
3558349cc55cSDimitry Andric         form_delegate_sp->GetName().c_str(), bounds, true);
3559349cc55cSDimitry Andric     WindowDelegateSP window_delegate_sp =
3560349cc55cSDimitry Andric         WindowDelegateSP(new FormWindowDelegate(form_delegate_sp));
3561349cc55cSDimitry Andric     form_window_sp->SetDelegate(window_delegate_sp);
3562349cc55cSDimitry Andric 
3563349cc55cSDimitry Andric     return true;
3564349cc55cSDimitry Andric   }
3565349cc55cSDimitry Andric 
GetTarget()3566349cc55cSDimitry Andric   Target *GetTarget() {
3567349cc55cSDimitry Andric     Target *target = m_debugger.GetSelectedTarget().get();
3568349cc55cSDimitry Andric 
3569349cc55cSDimitry Andric     if (target == nullptr) {
3570349cc55cSDimitry Andric       SetError("No target exists!");
3571349cc55cSDimitry Andric       return nullptr;
3572349cc55cSDimitry Andric     }
3573349cc55cSDimitry Andric 
3574349cc55cSDimitry Andric     ModuleSP exe_module_sp = target->GetExecutableModule();
3575349cc55cSDimitry Andric 
3576349cc55cSDimitry Andric     if (exe_module_sp == nullptr) {
3577349cc55cSDimitry Andric       SetError("No executable in target!");
3578349cc55cSDimitry Andric       return nullptr;
3579349cc55cSDimitry Andric     }
3580349cc55cSDimitry Andric 
3581349cc55cSDimitry Andric     return target;
3582349cc55cSDimitry Andric   }
3583349cc55cSDimitry Andric 
Launch(Window & window)3584349cc55cSDimitry Andric   void Launch(Window &window) {
3585349cc55cSDimitry Andric     ClearError();
3586349cc55cSDimitry Andric 
3587349cc55cSDimitry Andric     bool all_fields_are_valid = CheckFieldsValidity();
3588349cc55cSDimitry Andric     if (!all_fields_are_valid)
3589349cc55cSDimitry Andric       return;
3590349cc55cSDimitry Andric 
3591349cc55cSDimitry Andric     bool process_is_running = StopRunningProcess();
3592349cc55cSDimitry Andric     if (process_is_running)
3593349cc55cSDimitry Andric       return;
3594349cc55cSDimitry Andric 
3595349cc55cSDimitry Andric     Target *target = GetTarget();
3596349cc55cSDimitry Andric     if (HasError())
3597349cc55cSDimitry Andric       return;
3598349cc55cSDimitry Andric 
3599349cc55cSDimitry Andric     StreamString stream;
3600349cc55cSDimitry Andric     ProcessLaunchInfo launch_info = GetLaunchInfo();
3601349cc55cSDimitry Andric     Status status = target->Launch(launch_info, &stream);
3602349cc55cSDimitry Andric 
3603349cc55cSDimitry Andric     if (status.Fail()) {
3604349cc55cSDimitry Andric       SetError(status.AsCString());
3605349cc55cSDimitry Andric       return;
3606349cc55cSDimitry Andric     }
3607349cc55cSDimitry Andric 
3608349cc55cSDimitry Andric     ProcessSP process_sp(target->GetProcessSP());
3609349cc55cSDimitry Andric     if (!process_sp) {
3610349cc55cSDimitry Andric       SetError("Launched successfully but target has no process!");
3611349cc55cSDimitry Andric       return;
3612349cc55cSDimitry Andric     }
3613349cc55cSDimitry Andric 
3614349cc55cSDimitry Andric     window.GetParent()->RemoveSubWindow(&window);
3615349cc55cSDimitry Andric   }
3616349cc55cSDimitry Andric 
3617349cc55cSDimitry Andric protected:
3618349cc55cSDimitry Andric   Debugger &m_debugger;
3619349cc55cSDimitry Andric   WindowSP m_main_window_sp;
3620349cc55cSDimitry Andric 
3621349cc55cSDimitry Andric   ArgumentsFieldDelegate *m_arguments_field;
3622349cc55cSDimitry Andric   EnvironmentVariableListFieldDelegate *m_target_environment_field;
3623349cc55cSDimitry Andric   DirectoryFieldDelegate *m_working_directory_field;
3624349cc55cSDimitry Andric 
3625349cc55cSDimitry Andric   BooleanFieldDelegate *m_show_advanced_field;
3626349cc55cSDimitry Andric 
3627349cc55cSDimitry Andric   BooleanFieldDelegate *m_stop_at_entry_field;
3628349cc55cSDimitry Andric   BooleanFieldDelegate *m_detach_on_error_field;
3629349cc55cSDimitry Andric   BooleanFieldDelegate *m_disable_aslr_field;
3630349cc55cSDimitry Andric   ProcessPluginFieldDelegate *m_plugin_field;
3631349cc55cSDimitry Andric   ArchFieldDelegate *m_arch_field;
3632349cc55cSDimitry Andric   FileFieldDelegate *m_shell_field;
3633349cc55cSDimitry Andric   BooleanFieldDelegate *m_expand_shell_arguments_field;
3634349cc55cSDimitry Andric   BooleanFieldDelegate *m_disable_standard_io_field;
3635349cc55cSDimitry Andric   FileFieldDelegate *m_standard_input_field;
3636349cc55cSDimitry Andric   FileFieldDelegate *m_standard_output_field;
3637349cc55cSDimitry Andric   FileFieldDelegate *m_standard_error_field;
3638349cc55cSDimitry Andric 
3639349cc55cSDimitry Andric   BooleanFieldDelegate *m_show_inherited_environment_field;
3640349cc55cSDimitry Andric   EnvironmentVariableListFieldDelegate *m_inherited_environment_field;
3641349cc55cSDimitry Andric };
3642349cc55cSDimitry Andric 
3643349cc55cSDimitry Andric ////////////
3644349cc55cSDimitry Andric // Searchers
3645349cc55cSDimitry Andric ////////////
3646349cc55cSDimitry Andric 
3647349cc55cSDimitry Andric class SearcherDelegate {
3648349cc55cSDimitry Andric public:
364981ad6265SDimitry Andric   SearcherDelegate() = default;
3650349cc55cSDimitry Andric 
3651349cc55cSDimitry Andric   virtual ~SearcherDelegate() = default;
3652349cc55cSDimitry Andric 
3653349cc55cSDimitry Andric   virtual int GetNumberOfMatches() = 0;
3654349cc55cSDimitry Andric 
3655349cc55cSDimitry Andric   // Get the string that will be displayed for the match at the input index.
3656349cc55cSDimitry Andric   virtual const std::string &GetMatchTextAtIndex(int index) = 0;
3657349cc55cSDimitry Andric 
3658349cc55cSDimitry Andric   // Update the matches of the search. This is executed every time the text
3659349cc55cSDimitry Andric   // field handles an event.
3660349cc55cSDimitry Andric   virtual void UpdateMatches(const std::string &text) = 0;
3661349cc55cSDimitry Andric 
3662349cc55cSDimitry Andric   // Execute the user callback given the index of some match. This is executed
3663349cc55cSDimitry Andric   // once the user selects a match.
3664349cc55cSDimitry Andric   virtual void ExecuteCallback(int match_index) = 0;
3665349cc55cSDimitry Andric };
3666349cc55cSDimitry Andric 
3667349cc55cSDimitry Andric typedef std::shared_ptr<SearcherDelegate> SearcherDelegateSP;
3668349cc55cSDimitry Andric 
3669349cc55cSDimitry Andric class SearcherWindowDelegate : public WindowDelegate {
3670349cc55cSDimitry Andric public:
SearcherWindowDelegate(SearcherDelegateSP & delegate_sp)3671349cc55cSDimitry Andric   SearcherWindowDelegate(SearcherDelegateSP &delegate_sp)
367281ad6265SDimitry Andric       : m_delegate_sp(delegate_sp), m_text_field("Search", "", false) {
3673349cc55cSDimitry Andric     ;
3674349cc55cSDimitry Andric   }
3675349cc55cSDimitry Andric 
3676349cc55cSDimitry Andric   // A completion window is padded by one character from all sides. A text field
3677349cc55cSDimitry Andric   // is first drawn for inputting the searcher request, then a list of matches
3678349cc55cSDimitry Andric   // are displayed in a scrollable list.
3679349cc55cSDimitry Andric   //
3680349cc55cSDimitry Andric   // ___<Searcher Window Name>____________________________
3681349cc55cSDimitry Andric   // |                                                   |
3682349cc55cSDimitry Andric   // | __[Search]_______________________________________ |
3683349cc55cSDimitry Andric   // | |                                               | |
3684349cc55cSDimitry Andric   // | |_______________________________________________| |
3685349cc55cSDimitry Andric   // | - Match 1.                                        |
3686349cc55cSDimitry Andric   // | - Match 2.                                        |
3687349cc55cSDimitry Andric   // | - ...                                             |
3688349cc55cSDimitry Andric   // |                                                   |
3689349cc55cSDimitry Andric   // |____________________________[Press Esc to Cancel]__|
3690349cc55cSDimitry Andric   //
3691349cc55cSDimitry Andric 
3692349cc55cSDimitry Andric   // Get the index of the last visible match. Assuming at least one match
3693349cc55cSDimitry Andric   // exists.
GetLastVisibleMatch(int height)3694349cc55cSDimitry Andric   int GetLastVisibleMatch(int height) {
3695349cc55cSDimitry Andric     int index = m_first_visible_match + height;
3696349cc55cSDimitry Andric     return std::min(index, m_delegate_sp->GetNumberOfMatches()) - 1;
3697349cc55cSDimitry Andric   }
3698349cc55cSDimitry Andric 
GetNumberOfVisibleMatches(int height)3699349cc55cSDimitry Andric   int GetNumberOfVisibleMatches(int height) {
3700349cc55cSDimitry Andric     return GetLastVisibleMatch(height) - m_first_visible_match + 1;
3701349cc55cSDimitry Andric   }
3702349cc55cSDimitry Andric 
UpdateScrolling(Surface & surface)3703349cc55cSDimitry Andric   void UpdateScrolling(Surface &surface) {
3704349cc55cSDimitry Andric     if (m_selected_match < m_first_visible_match) {
3705349cc55cSDimitry Andric       m_first_visible_match = m_selected_match;
3706349cc55cSDimitry Andric       return;
3707349cc55cSDimitry Andric     }
3708349cc55cSDimitry Andric 
3709349cc55cSDimitry Andric     int height = surface.GetHeight();
3710349cc55cSDimitry Andric     int last_visible_match = GetLastVisibleMatch(height);
3711349cc55cSDimitry Andric     if (m_selected_match > last_visible_match) {
3712349cc55cSDimitry Andric       m_first_visible_match = m_selected_match - height + 1;
3713349cc55cSDimitry Andric     }
3714349cc55cSDimitry Andric   }
3715349cc55cSDimitry Andric 
DrawMatches(Surface & surface)3716349cc55cSDimitry Andric   void DrawMatches(Surface &surface) {
3717349cc55cSDimitry Andric     if (m_delegate_sp->GetNumberOfMatches() == 0)
3718349cc55cSDimitry Andric       return;
3719349cc55cSDimitry Andric 
3720349cc55cSDimitry Andric     UpdateScrolling(surface);
3721349cc55cSDimitry Andric 
3722349cc55cSDimitry Andric     int count = GetNumberOfVisibleMatches(surface.GetHeight());
3723349cc55cSDimitry Andric     for (int i = 0; i < count; i++) {
3724349cc55cSDimitry Andric       surface.MoveCursor(1, i);
3725349cc55cSDimitry Andric       int current_match = m_first_visible_match + i;
3726349cc55cSDimitry Andric       if (current_match == m_selected_match)
3727349cc55cSDimitry Andric         surface.AttributeOn(A_REVERSE);
3728349cc55cSDimitry Andric       surface.PutCString(
3729349cc55cSDimitry Andric           m_delegate_sp->GetMatchTextAtIndex(current_match).c_str());
3730349cc55cSDimitry Andric       if (current_match == m_selected_match)
3731349cc55cSDimitry Andric         surface.AttributeOff(A_REVERSE);
3732349cc55cSDimitry Andric     }
3733349cc55cSDimitry Andric   }
3734349cc55cSDimitry Andric 
DrawContent(Surface & surface)3735349cc55cSDimitry Andric   void DrawContent(Surface &surface) {
3736349cc55cSDimitry Andric     Rect content_bounds = surface.GetFrame();
3737349cc55cSDimitry Andric     Rect text_field_bounds, matchs_bounds;
3738349cc55cSDimitry Andric     content_bounds.HorizontalSplit(m_text_field.FieldDelegateGetHeight(),
3739349cc55cSDimitry Andric                                    text_field_bounds, matchs_bounds);
3740349cc55cSDimitry Andric     Surface text_field_surface = surface.SubSurface(text_field_bounds);
3741349cc55cSDimitry Andric     Surface matches_surface = surface.SubSurface(matchs_bounds);
3742349cc55cSDimitry Andric 
3743349cc55cSDimitry Andric     m_text_field.FieldDelegateDraw(text_field_surface, true);
3744349cc55cSDimitry Andric     DrawMatches(matches_surface);
3745349cc55cSDimitry Andric   }
3746349cc55cSDimitry Andric 
WindowDelegateDraw(Window & window,bool force)3747349cc55cSDimitry Andric   bool WindowDelegateDraw(Window &window, bool force) override {
3748349cc55cSDimitry Andric     window.Erase();
3749349cc55cSDimitry Andric 
3750349cc55cSDimitry Andric     window.DrawTitleBox(window.GetName(), "Press Esc to Cancel");
3751349cc55cSDimitry Andric 
3752349cc55cSDimitry Andric     Rect content_bounds = window.GetFrame();
3753349cc55cSDimitry Andric     content_bounds.Inset(2, 2);
3754349cc55cSDimitry Andric     Surface content_surface = window.SubSurface(content_bounds);
3755349cc55cSDimitry Andric 
3756349cc55cSDimitry Andric     DrawContent(content_surface);
3757349cc55cSDimitry Andric     return true;
3758349cc55cSDimitry Andric   }
3759349cc55cSDimitry Andric 
SelectNext()3760349cc55cSDimitry Andric   void SelectNext() {
3761349cc55cSDimitry Andric     if (m_selected_match != m_delegate_sp->GetNumberOfMatches() - 1)
3762349cc55cSDimitry Andric       m_selected_match++;
3763349cc55cSDimitry Andric   }
3764349cc55cSDimitry Andric 
SelectPrevious()3765349cc55cSDimitry Andric   void SelectPrevious() {
3766349cc55cSDimitry Andric     if (m_selected_match != 0)
3767349cc55cSDimitry Andric       m_selected_match--;
3768349cc55cSDimitry Andric   }
3769349cc55cSDimitry Andric 
ExecuteCallback(Window & window)3770349cc55cSDimitry Andric   void ExecuteCallback(Window &window) {
3771349cc55cSDimitry Andric     m_delegate_sp->ExecuteCallback(m_selected_match);
3772349cc55cSDimitry Andric     window.GetParent()->RemoveSubWindow(&window);
3773349cc55cSDimitry Andric   }
3774349cc55cSDimitry Andric 
UpdateMatches()3775349cc55cSDimitry Andric   void UpdateMatches() {
3776349cc55cSDimitry Andric     m_delegate_sp->UpdateMatches(m_text_field.GetText());
3777349cc55cSDimitry Andric     m_selected_match = 0;
3778349cc55cSDimitry Andric   }
3779349cc55cSDimitry Andric 
WindowDelegateHandleChar(Window & window,int key)3780349cc55cSDimitry Andric   HandleCharResult WindowDelegateHandleChar(Window &window, int key) override {
3781349cc55cSDimitry Andric     switch (key) {
3782349cc55cSDimitry Andric     case '\r':
3783349cc55cSDimitry Andric     case '\n':
3784349cc55cSDimitry Andric     case KEY_ENTER:
3785349cc55cSDimitry Andric       ExecuteCallback(window);
3786349cc55cSDimitry Andric       return eKeyHandled;
3787349cc55cSDimitry Andric     case '\t':
3788349cc55cSDimitry Andric     case KEY_DOWN:
3789349cc55cSDimitry Andric       SelectNext();
3790349cc55cSDimitry Andric       return eKeyHandled;
3791349cc55cSDimitry Andric     case KEY_SHIFT_TAB:
3792349cc55cSDimitry Andric     case KEY_UP:
3793349cc55cSDimitry Andric       SelectPrevious();
3794349cc55cSDimitry Andric       return eKeyHandled;
3795349cc55cSDimitry Andric     case KEY_ESCAPE:
3796349cc55cSDimitry Andric       window.GetParent()->RemoveSubWindow(&window);
3797349cc55cSDimitry Andric       return eKeyHandled;
3798349cc55cSDimitry Andric     default:
3799349cc55cSDimitry Andric       break;
3800349cc55cSDimitry Andric     }
3801349cc55cSDimitry Andric 
3802349cc55cSDimitry Andric     if (m_text_field.FieldDelegateHandleChar(key) == eKeyHandled)
3803349cc55cSDimitry Andric       UpdateMatches();
3804349cc55cSDimitry Andric 
3805349cc55cSDimitry Andric     return eKeyHandled;
3806349cc55cSDimitry Andric   }
3807349cc55cSDimitry Andric 
3808349cc55cSDimitry Andric protected:
3809349cc55cSDimitry Andric   SearcherDelegateSP m_delegate_sp;
3810349cc55cSDimitry Andric   TextFieldDelegate m_text_field;
3811349cc55cSDimitry Andric   // The index of the currently selected match.
381281ad6265SDimitry Andric   int m_selected_match = 0;
3813349cc55cSDimitry Andric   // The index of the first visible match.
381481ad6265SDimitry Andric   int m_first_visible_match = 0;
3815349cc55cSDimitry Andric };
3816349cc55cSDimitry Andric 
3817349cc55cSDimitry Andric //////////////////////////////
3818349cc55cSDimitry Andric // Searcher Delegate Instances
3819349cc55cSDimitry Andric //////////////////////////////
3820349cc55cSDimitry Andric 
3821349cc55cSDimitry Andric // This is a searcher delegate wrapper around CommandCompletions common
3822349cc55cSDimitry Andric // callbacks. The callbacks are only given the match string. The completion_mask
382306c3fb27SDimitry Andric // can be a combination of lldb::CompletionType.
3824349cc55cSDimitry Andric class CommonCompletionSearcherDelegate : public SearcherDelegate {
3825349cc55cSDimitry Andric public:
3826349cc55cSDimitry Andric   typedef std::function<void(const std::string &)> CallbackType;
3827349cc55cSDimitry Andric 
CommonCompletionSearcherDelegate(Debugger & debugger,uint32_t completion_mask,CallbackType callback)3828349cc55cSDimitry Andric   CommonCompletionSearcherDelegate(Debugger &debugger, uint32_t completion_mask,
3829349cc55cSDimitry Andric                                    CallbackType callback)
3830349cc55cSDimitry Andric       : m_debugger(debugger), m_completion_mask(completion_mask),
3831349cc55cSDimitry Andric         m_callback(callback) {}
3832349cc55cSDimitry Andric 
GetNumberOfMatches()3833349cc55cSDimitry Andric   int GetNumberOfMatches() override { return m_matches.GetSize(); }
3834349cc55cSDimitry Andric 
GetMatchTextAtIndex(int index)3835349cc55cSDimitry Andric   const std::string &GetMatchTextAtIndex(int index) override {
3836349cc55cSDimitry Andric     return m_matches[index];
3837349cc55cSDimitry Andric   }
3838349cc55cSDimitry Andric 
UpdateMatches(const std::string & text)3839349cc55cSDimitry Andric   void UpdateMatches(const std::string &text) override {
3840349cc55cSDimitry Andric     CompletionResult result;
3841349cc55cSDimitry Andric     CompletionRequest request(text.c_str(), text.size(), result);
384206c3fb27SDimitry Andric     lldb_private::CommandCompletions::InvokeCommonCompletionCallbacks(
3843349cc55cSDimitry Andric         m_debugger.GetCommandInterpreter(), m_completion_mask, request,
3844349cc55cSDimitry Andric         nullptr);
3845349cc55cSDimitry Andric     result.GetMatches(m_matches);
3846349cc55cSDimitry Andric   }
3847349cc55cSDimitry Andric 
ExecuteCallback(int match_index)3848349cc55cSDimitry Andric   void ExecuteCallback(int match_index) override {
3849349cc55cSDimitry Andric     m_callback(m_matches[match_index]);
3850349cc55cSDimitry Andric   }
3851349cc55cSDimitry Andric 
3852349cc55cSDimitry Andric protected:
3853349cc55cSDimitry Andric   Debugger &m_debugger;
385406c3fb27SDimitry Andric   // A compound mask from lldb::CompletionType.
3855349cc55cSDimitry Andric   uint32_t m_completion_mask;
3856349cc55cSDimitry Andric   // A callback to execute once the user selects a match. The match is passed to
3857349cc55cSDimitry Andric   // the callback as a string.
3858349cc55cSDimitry Andric   CallbackType m_callback;
3859349cc55cSDimitry Andric   StringList m_matches;
3860349cc55cSDimitry Andric };
3861349cc55cSDimitry Andric 
3862349cc55cSDimitry Andric ////////
3863349cc55cSDimitry Andric // Menus
3864349cc55cSDimitry Andric ////////
3865349cc55cSDimitry Andric 
3866480093f4SDimitry Andric class MenuDelegate {
3867480093f4SDimitry Andric public:
3868480093f4SDimitry Andric   virtual ~MenuDelegate() = default;
3869480093f4SDimitry Andric 
3870480093f4SDimitry Andric   virtual MenuActionResult MenuDelegateAction(Menu &menu) = 0;
3871480093f4SDimitry Andric };
3872480093f4SDimitry Andric 
3873480093f4SDimitry Andric class Menu : public WindowDelegate {
3874480093f4SDimitry Andric public:
3875480093f4SDimitry Andric   enum class Type { Invalid, Bar, Item, Separator };
3876480093f4SDimitry Andric 
3877480093f4SDimitry Andric   // Menubar or separator constructor
3878480093f4SDimitry Andric   Menu(Type type);
3879480093f4SDimitry Andric 
3880480093f4SDimitry Andric   // Menuitem constructor
3881480093f4SDimitry Andric   Menu(const char *name, const char *key_name, int key_value,
3882480093f4SDimitry Andric        uint64_t identifier);
3883480093f4SDimitry Andric 
3884480093f4SDimitry Andric   ~Menu() override = default;
3885480093f4SDimitry Andric 
GetDelegate() const3886480093f4SDimitry Andric   const MenuDelegateSP &GetDelegate() const { return m_delegate_sp; }
3887480093f4SDimitry Andric 
SetDelegate(const MenuDelegateSP & delegate_sp)3888480093f4SDimitry Andric   void SetDelegate(const MenuDelegateSP &delegate_sp) {
3889480093f4SDimitry Andric     m_delegate_sp = delegate_sp;
3890480093f4SDimitry Andric   }
3891480093f4SDimitry Andric 
3892480093f4SDimitry Andric   void RecalculateNameLengths();
3893480093f4SDimitry Andric 
3894480093f4SDimitry Andric   void AddSubmenu(const MenuSP &menu_sp);
3895480093f4SDimitry Andric 
3896480093f4SDimitry Andric   int DrawAndRunMenu(Window &window);
3897480093f4SDimitry Andric 
3898480093f4SDimitry Andric   void DrawMenuTitle(Window &window, bool highlight);
3899480093f4SDimitry Andric 
3900480093f4SDimitry Andric   bool WindowDelegateDraw(Window &window, bool force) override;
3901480093f4SDimitry Andric 
3902480093f4SDimitry Andric   HandleCharResult WindowDelegateHandleChar(Window &window, int key) override;
3903480093f4SDimitry Andric 
ActionPrivate(Menu & menu)3904480093f4SDimitry Andric   MenuActionResult ActionPrivate(Menu &menu) {
3905480093f4SDimitry Andric     MenuActionResult result = MenuActionResult::NotHandled;
3906480093f4SDimitry Andric     if (m_delegate_sp) {
3907480093f4SDimitry Andric       result = m_delegate_sp->MenuDelegateAction(menu);
3908480093f4SDimitry Andric       if (result != MenuActionResult::NotHandled)
3909480093f4SDimitry Andric         return result;
3910480093f4SDimitry Andric     } else if (m_parent) {
3911480093f4SDimitry Andric       result = m_parent->ActionPrivate(menu);
3912480093f4SDimitry Andric       if (result != MenuActionResult::NotHandled)
3913480093f4SDimitry Andric         return result;
3914480093f4SDimitry Andric     }
3915480093f4SDimitry Andric     return m_canned_result;
3916480093f4SDimitry Andric   }
3917480093f4SDimitry Andric 
Action()3918480093f4SDimitry Andric   MenuActionResult Action() {
3919480093f4SDimitry Andric     // Call the recursive action so it can try to handle it with the menu
3920480093f4SDimitry Andric     // delegate, and if not, try our parent menu
3921480093f4SDimitry Andric     return ActionPrivate(*this);
3922480093f4SDimitry Andric   }
3923480093f4SDimitry Andric 
SetCannedResult(MenuActionResult result)3924480093f4SDimitry Andric   void SetCannedResult(MenuActionResult result) { m_canned_result = result; }
3925480093f4SDimitry Andric 
GetSubmenus()3926480093f4SDimitry Andric   Menus &GetSubmenus() { return m_submenus; }
3927480093f4SDimitry Andric 
GetSubmenus() const3928480093f4SDimitry Andric   const Menus &GetSubmenus() const { return m_submenus; }
3929480093f4SDimitry Andric 
GetSelectedSubmenuIndex() const3930480093f4SDimitry Andric   int GetSelectedSubmenuIndex() const { return m_selected; }
3931480093f4SDimitry Andric 
SetSelectedSubmenuIndex(int idx)3932480093f4SDimitry Andric   void SetSelectedSubmenuIndex(int idx) { m_selected = idx; }
3933480093f4SDimitry Andric 
GetType() const3934480093f4SDimitry Andric   Type GetType() const { return m_type; }
3935480093f4SDimitry Andric 
GetStartingColumn() const3936480093f4SDimitry Andric   int GetStartingColumn() const { return m_start_col; }
3937480093f4SDimitry Andric 
SetStartingColumn(int col)3938480093f4SDimitry Andric   void SetStartingColumn(int col) { m_start_col = col; }
3939480093f4SDimitry Andric 
GetKeyValue() const3940480093f4SDimitry Andric   int GetKeyValue() const { return m_key_value; }
3941480093f4SDimitry Andric 
GetName()3942480093f4SDimitry Andric   std::string &GetName() { return m_name; }
3943480093f4SDimitry Andric 
GetDrawWidth() const3944480093f4SDimitry Andric   int GetDrawWidth() const {
3945480093f4SDimitry Andric     return m_max_submenu_name_length + m_max_submenu_key_name_length + 8;
3946480093f4SDimitry Andric   }
3947480093f4SDimitry Andric 
GetIdentifier() const3948480093f4SDimitry Andric   uint64_t GetIdentifier() const { return m_identifier; }
3949480093f4SDimitry Andric 
SetIdentifier(uint64_t identifier)3950480093f4SDimitry Andric   void SetIdentifier(uint64_t identifier) { m_identifier = identifier; }
3951480093f4SDimitry Andric 
3952480093f4SDimitry Andric protected:
3953480093f4SDimitry Andric   std::string m_name;
3954480093f4SDimitry Andric   std::string m_key_name;
3955480093f4SDimitry Andric   uint64_t m_identifier;
3956480093f4SDimitry Andric   Type m_type;
3957480093f4SDimitry Andric   int m_key_value;
3958480093f4SDimitry Andric   int m_start_col;
3959480093f4SDimitry Andric   int m_max_submenu_name_length;
3960480093f4SDimitry Andric   int m_max_submenu_key_name_length;
3961480093f4SDimitry Andric   int m_selected;
3962480093f4SDimitry Andric   Menu *m_parent;
3963480093f4SDimitry Andric   Menus m_submenus;
3964480093f4SDimitry Andric   WindowSP m_menu_window_sp;
3965480093f4SDimitry Andric   MenuActionResult m_canned_result;
3966480093f4SDimitry Andric   MenuDelegateSP m_delegate_sp;
3967480093f4SDimitry Andric };
3968480093f4SDimitry Andric 
3969480093f4SDimitry Andric // Menubar or separator constructor
Menu(Type type)3970480093f4SDimitry Andric Menu::Menu(Type type)
3971480093f4SDimitry Andric     : m_name(), m_key_name(), m_identifier(0), m_type(type), m_key_value(0),
3972480093f4SDimitry Andric       m_start_col(0), m_max_submenu_name_length(0),
3973480093f4SDimitry Andric       m_max_submenu_key_name_length(0), m_selected(0), m_parent(nullptr),
3974480093f4SDimitry Andric       m_submenus(), m_canned_result(MenuActionResult::NotHandled),
3975480093f4SDimitry Andric       m_delegate_sp() {}
3976480093f4SDimitry Andric 
3977480093f4SDimitry Andric // Menuitem constructor
Menu(const char * name,const char * key_name,int key_value,uint64_t identifier)3978480093f4SDimitry Andric Menu::Menu(const char *name, const char *key_name, int key_value,
3979480093f4SDimitry Andric            uint64_t identifier)
3980480093f4SDimitry Andric     : m_name(), m_key_name(), m_identifier(identifier), m_type(Type::Invalid),
3981480093f4SDimitry Andric       m_key_value(key_value), m_start_col(0), m_max_submenu_name_length(0),
3982480093f4SDimitry Andric       m_max_submenu_key_name_length(0), m_selected(0), m_parent(nullptr),
3983480093f4SDimitry Andric       m_submenus(), m_canned_result(MenuActionResult::NotHandled),
3984480093f4SDimitry Andric       m_delegate_sp() {
3985480093f4SDimitry Andric   if (name && name[0]) {
3986480093f4SDimitry Andric     m_name = name;
3987480093f4SDimitry Andric     m_type = Type::Item;
3988480093f4SDimitry Andric     if (key_name && key_name[0])
3989480093f4SDimitry Andric       m_key_name = key_name;
3990480093f4SDimitry Andric   } else {
3991480093f4SDimitry Andric     m_type = Type::Separator;
3992480093f4SDimitry Andric   }
3993480093f4SDimitry Andric }
3994480093f4SDimitry Andric 
RecalculateNameLengths()3995480093f4SDimitry Andric void Menu::RecalculateNameLengths() {
3996480093f4SDimitry Andric   m_max_submenu_name_length = 0;
3997480093f4SDimitry Andric   m_max_submenu_key_name_length = 0;
3998480093f4SDimitry Andric   Menus &submenus = GetSubmenus();
3999480093f4SDimitry Andric   const size_t num_submenus = submenus.size();
4000480093f4SDimitry Andric   for (size_t i = 0; i < num_submenus; ++i) {
4001480093f4SDimitry Andric     Menu *submenu = submenus[i].get();
4002480093f4SDimitry Andric     if (static_cast<size_t>(m_max_submenu_name_length) < submenu->m_name.size())
4003480093f4SDimitry Andric       m_max_submenu_name_length = submenu->m_name.size();
4004480093f4SDimitry Andric     if (static_cast<size_t>(m_max_submenu_key_name_length) <
4005480093f4SDimitry Andric         submenu->m_key_name.size())
4006480093f4SDimitry Andric       m_max_submenu_key_name_length = submenu->m_key_name.size();
4007480093f4SDimitry Andric   }
4008480093f4SDimitry Andric }
4009480093f4SDimitry Andric 
AddSubmenu(const MenuSP & menu_sp)4010480093f4SDimitry Andric void Menu::AddSubmenu(const MenuSP &menu_sp) {
4011480093f4SDimitry Andric   menu_sp->m_parent = this;
4012480093f4SDimitry Andric   if (static_cast<size_t>(m_max_submenu_name_length) < menu_sp->m_name.size())
4013480093f4SDimitry Andric     m_max_submenu_name_length = menu_sp->m_name.size();
4014480093f4SDimitry Andric   if (static_cast<size_t>(m_max_submenu_key_name_length) <
4015480093f4SDimitry Andric       menu_sp->m_key_name.size())
4016480093f4SDimitry Andric     m_max_submenu_key_name_length = menu_sp->m_key_name.size();
4017480093f4SDimitry Andric   m_submenus.push_back(menu_sp);
4018480093f4SDimitry Andric }
4019480093f4SDimitry Andric 
DrawMenuTitle(Window & window,bool highlight)4020480093f4SDimitry Andric void Menu::DrawMenuTitle(Window &window, bool highlight) {
4021480093f4SDimitry Andric   if (m_type == Type::Separator) {
4022480093f4SDimitry Andric     window.MoveCursor(0, window.GetCursorY());
4023480093f4SDimitry Andric     window.PutChar(ACS_LTEE);
4024480093f4SDimitry Andric     int width = window.GetWidth();
4025480093f4SDimitry Andric     if (width > 2) {
4026480093f4SDimitry Andric       width -= 2;
4027480093f4SDimitry Andric       for (int i = 0; i < width; ++i)
4028480093f4SDimitry Andric         window.PutChar(ACS_HLINE);
4029480093f4SDimitry Andric     }
4030480093f4SDimitry Andric     window.PutChar(ACS_RTEE);
4031480093f4SDimitry Andric   } else {
4032480093f4SDimitry Andric     const int shortcut_key = m_key_value;
4033480093f4SDimitry Andric     bool underlined_shortcut = false;
4034e8d8bef9SDimitry Andric     const attr_t highlight_attr = A_REVERSE;
4035480093f4SDimitry Andric     if (highlight)
4036e8d8bef9SDimitry Andric       window.AttributeOn(highlight_attr);
40375ffd83dbSDimitry Andric     if (llvm::isPrint(shortcut_key)) {
4038480093f4SDimitry Andric       size_t lower_pos = m_name.find(tolower(shortcut_key));
4039480093f4SDimitry Andric       size_t upper_pos = m_name.find(toupper(shortcut_key));
4040480093f4SDimitry Andric       const char *name = m_name.c_str();
4041480093f4SDimitry Andric       size_t pos = std::min<size_t>(lower_pos, upper_pos);
4042480093f4SDimitry Andric       if (pos != std::string::npos) {
4043480093f4SDimitry Andric         underlined_shortcut = true;
4044480093f4SDimitry Andric         if (pos > 0) {
4045480093f4SDimitry Andric           window.PutCString(name, pos);
4046480093f4SDimitry Andric           name += pos;
4047480093f4SDimitry Andric         }
4048480093f4SDimitry Andric         const attr_t shortcut_attr = A_UNDERLINE | A_BOLD;
4049480093f4SDimitry Andric         window.AttributeOn(shortcut_attr);
4050480093f4SDimitry Andric         window.PutChar(name[0]);
4051480093f4SDimitry Andric         window.AttributeOff(shortcut_attr);
4052480093f4SDimitry Andric         name++;
4053480093f4SDimitry Andric         if (name[0])
4054480093f4SDimitry Andric           window.PutCString(name);
4055480093f4SDimitry Andric       }
4056480093f4SDimitry Andric     }
4057480093f4SDimitry Andric 
4058480093f4SDimitry Andric     if (!underlined_shortcut) {
4059480093f4SDimitry Andric       window.PutCString(m_name.c_str());
4060480093f4SDimitry Andric     }
4061480093f4SDimitry Andric 
4062480093f4SDimitry Andric     if (highlight)
4063e8d8bef9SDimitry Andric       window.AttributeOff(highlight_attr);
4064480093f4SDimitry Andric 
4065480093f4SDimitry Andric     if (m_key_name.empty()) {
40665ffd83dbSDimitry Andric       if (!underlined_shortcut && llvm::isPrint(m_key_value)) {
4067e8d8bef9SDimitry Andric         window.AttributeOn(COLOR_PAIR(MagentaOnWhite));
4068480093f4SDimitry Andric         window.Printf(" (%c)", m_key_value);
4069e8d8bef9SDimitry Andric         window.AttributeOff(COLOR_PAIR(MagentaOnWhite));
4070480093f4SDimitry Andric       }
4071480093f4SDimitry Andric     } else {
4072e8d8bef9SDimitry Andric       window.AttributeOn(COLOR_PAIR(MagentaOnWhite));
4073480093f4SDimitry Andric       window.Printf(" (%s)", m_key_name.c_str());
4074e8d8bef9SDimitry Andric       window.AttributeOff(COLOR_PAIR(MagentaOnWhite));
4075480093f4SDimitry Andric     }
4076480093f4SDimitry Andric   }
4077480093f4SDimitry Andric }
4078480093f4SDimitry Andric 
WindowDelegateDraw(Window & window,bool force)4079480093f4SDimitry Andric bool Menu::WindowDelegateDraw(Window &window, bool force) {
4080480093f4SDimitry Andric   Menus &submenus = GetSubmenus();
4081480093f4SDimitry Andric   const size_t num_submenus = submenus.size();
4082480093f4SDimitry Andric   const int selected_idx = GetSelectedSubmenuIndex();
4083480093f4SDimitry Andric   Menu::Type menu_type = GetType();
4084480093f4SDimitry Andric   switch (menu_type) {
4085480093f4SDimitry Andric   case Menu::Type::Bar: {
4086e8d8bef9SDimitry Andric     window.SetBackground(BlackOnWhite);
4087480093f4SDimitry Andric     window.MoveCursor(0, 0);
4088480093f4SDimitry Andric     for (size_t i = 0; i < num_submenus; ++i) {
4089480093f4SDimitry Andric       Menu *menu = submenus[i].get();
4090480093f4SDimitry Andric       if (i > 0)
4091480093f4SDimitry Andric         window.PutChar(' ');
4092480093f4SDimitry Andric       menu->SetStartingColumn(window.GetCursorX());
4093480093f4SDimitry Andric       window.PutCString("| ");
4094480093f4SDimitry Andric       menu->DrawMenuTitle(window, false);
4095480093f4SDimitry Andric     }
4096480093f4SDimitry Andric     window.PutCString(" |");
4097480093f4SDimitry Andric   } break;
4098480093f4SDimitry Andric 
4099480093f4SDimitry Andric   case Menu::Type::Item: {
4100480093f4SDimitry Andric     int y = 1;
4101480093f4SDimitry Andric     int x = 3;
4102480093f4SDimitry Andric     // Draw the menu
4103480093f4SDimitry Andric     int cursor_x = 0;
4104480093f4SDimitry Andric     int cursor_y = 0;
4105480093f4SDimitry Andric     window.Erase();
4106e8d8bef9SDimitry Andric     window.SetBackground(BlackOnWhite);
4107480093f4SDimitry Andric     window.Box();
4108480093f4SDimitry Andric     for (size_t i = 0; i < num_submenus; ++i) {
4109480093f4SDimitry Andric       const bool is_selected = (i == static_cast<size_t>(selected_idx));
4110480093f4SDimitry Andric       window.MoveCursor(x, y + i);
4111480093f4SDimitry Andric       if (is_selected) {
4112480093f4SDimitry Andric         // Remember where we want the cursor to be
4113480093f4SDimitry Andric         cursor_x = x - 1;
4114480093f4SDimitry Andric         cursor_y = y + i;
4115480093f4SDimitry Andric       }
4116480093f4SDimitry Andric       submenus[i]->DrawMenuTitle(window, is_selected);
4117480093f4SDimitry Andric     }
4118480093f4SDimitry Andric     window.MoveCursor(cursor_x, cursor_y);
4119480093f4SDimitry Andric   } break;
4120480093f4SDimitry Andric 
4121480093f4SDimitry Andric   default:
4122480093f4SDimitry Andric   case Menu::Type::Separator:
4123480093f4SDimitry Andric     break;
4124480093f4SDimitry Andric   }
4125480093f4SDimitry Andric   return true; // Drawing handled...
4126480093f4SDimitry Andric }
4127480093f4SDimitry Andric 
WindowDelegateHandleChar(Window & window,int key)4128480093f4SDimitry Andric HandleCharResult Menu::WindowDelegateHandleChar(Window &window, int key) {
4129480093f4SDimitry Andric   HandleCharResult result = eKeyNotHandled;
4130480093f4SDimitry Andric 
4131480093f4SDimitry Andric   Menus &submenus = GetSubmenus();
4132480093f4SDimitry Andric   const size_t num_submenus = submenus.size();
4133480093f4SDimitry Andric   const int selected_idx = GetSelectedSubmenuIndex();
4134480093f4SDimitry Andric   Menu::Type menu_type = GetType();
4135480093f4SDimitry Andric   if (menu_type == Menu::Type::Bar) {
4136480093f4SDimitry Andric     MenuSP run_menu_sp;
4137480093f4SDimitry Andric     switch (key) {
4138480093f4SDimitry Andric     case KEY_DOWN:
4139480093f4SDimitry Andric     case KEY_UP:
4140480093f4SDimitry Andric       // Show last menu or first menu
4141480093f4SDimitry Andric       if (selected_idx < static_cast<int>(num_submenus))
4142480093f4SDimitry Andric         run_menu_sp = submenus[selected_idx];
4143480093f4SDimitry Andric       else if (!submenus.empty())
4144480093f4SDimitry Andric         run_menu_sp = submenus.front();
4145480093f4SDimitry Andric       result = eKeyHandled;
4146480093f4SDimitry Andric       break;
4147480093f4SDimitry Andric 
4148480093f4SDimitry Andric     case KEY_RIGHT:
4149480093f4SDimitry Andric       ++m_selected;
4150480093f4SDimitry Andric       if (m_selected >= static_cast<int>(num_submenus))
4151480093f4SDimitry Andric         m_selected = 0;
4152480093f4SDimitry Andric       if (m_selected < static_cast<int>(num_submenus))
4153480093f4SDimitry Andric         run_menu_sp = submenus[m_selected];
4154480093f4SDimitry Andric       else if (!submenus.empty())
4155480093f4SDimitry Andric         run_menu_sp = submenus.front();
4156480093f4SDimitry Andric       result = eKeyHandled;
4157480093f4SDimitry Andric       break;
4158480093f4SDimitry Andric 
4159480093f4SDimitry Andric     case KEY_LEFT:
4160480093f4SDimitry Andric       --m_selected;
4161480093f4SDimitry Andric       if (m_selected < 0)
4162480093f4SDimitry Andric         m_selected = num_submenus - 1;
4163480093f4SDimitry Andric       if (m_selected < static_cast<int>(num_submenus))
4164480093f4SDimitry Andric         run_menu_sp = submenus[m_selected];
4165480093f4SDimitry Andric       else if (!submenus.empty())
4166480093f4SDimitry Andric         run_menu_sp = submenus.front();
4167480093f4SDimitry Andric       result = eKeyHandled;
4168480093f4SDimitry Andric       break;
4169480093f4SDimitry Andric 
4170480093f4SDimitry Andric     default:
4171480093f4SDimitry Andric       for (size_t i = 0; i < num_submenus; ++i) {
4172480093f4SDimitry Andric         if (submenus[i]->GetKeyValue() == key) {
4173480093f4SDimitry Andric           SetSelectedSubmenuIndex(i);
4174480093f4SDimitry Andric           run_menu_sp = submenus[i];
4175480093f4SDimitry Andric           result = eKeyHandled;
4176480093f4SDimitry Andric           break;
4177480093f4SDimitry Andric         }
4178480093f4SDimitry Andric       }
4179480093f4SDimitry Andric       break;
4180480093f4SDimitry Andric     }
4181480093f4SDimitry Andric 
4182480093f4SDimitry Andric     if (run_menu_sp) {
4183480093f4SDimitry Andric       // Run the action on this menu in case we need to populate the menu with
4184480093f4SDimitry Andric       // dynamic content and also in case check marks, and any other menu
4185480093f4SDimitry Andric       // decorations need to be calculated
4186480093f4SDimitry Andric       if (run_menu_sp->Action() == MenuActionResult::Quit)
4187480093f4SDimitry Andric         return eQuitApplication;
4188480093f4SDimitry Andric 
4189480093f4SDimitry Andric       Rect menu_bounds;
4190480093f4SDimitry Andric       menu_bounds.origin.x = run_menu_sp->GetStartingColumn();
4191480093f4SDimitry Andric       menu_bounds.origin.y = 1;
4192480093f4SDimitry Andric       menu_bounds.size.width = run_menu_sp->GetDrawWidth();
4193480093f4SDimitry Andric       menu_bounds.size.height = run_menu_sp->GetSubmenus().size() + 2;
4194480093f4SDimitry Andric       if (m_menu_window_sp)
4195480093f4SDimitry Andric         window.GetParent()->RemoveSubWindow(m_menu_window_sp.get());
4196480093f4SDimitry Andric 
4197480093f4SDimitry Andric       m_menu_window_sp = window.GetParent()->CreateSubWindow(
4198480093f4SDimitry Andric           run_menu_sp->GetName().c_str(), menu_bounds, true);
4199480093f4SDimitry Andric       m_menu_window_sp->SetDelegate(run_menu_sp);
4200480093f4SDimitry Andric     }
4201480093f4SDimitry Andric   } else if (menu_type == Menu::Type::Item) {
4202480093f4SDimitry Andric     switch (key) {
4203480093f4SDimitry Andric     case KEY_DOWN:
4204480093f4SDimitry Andric       if (m_submenus.size() > 1) {
4205480093f4SDimitry Andric         const int start_select = m_selected;
4206480093f4SDimitry Andric         while (++m_selected != start_select) {
4207480093f4SDimitry Andric           if (static_cast<size_t>(m_selected) >= num_submenus)
4208480093f4SDimitry Andric             m_selected = 0;
4209480093f4SDimitry Andric           if (m_submenus[m_selected]->GetType() == Type::Separator)
4210480093f4SDimitry Andric             continue;
4211480093f4SDimitry Andric           else
4212480093f4SDimitry Andric             break;
4213480093f4SDimitry Andric         }
4214480093f4SDimitry Andric         return eKeyHandled;
4215480093f4SDimitry Andric       }
4216480093f4SDimitry Andric       break;
4217480093f4SDimitry Andric 
4218480093f4SDimitry Andric     case KEY_UP:
4219480093f4SDimitry Andric       if (m_submenus.size() > 1) {
4220480093f4SDimitry Andric         const int start_select = m_selected;
4221480093f4SDimitry Andric         while (--m_selected != start_select) {
4222480093f4SDimitry Andric           if (m_selected < static_cast<int>(0))
4223480093f4SDimitry Andric             m_selected = num_submenus - 1;
4224480093f4SDimitry Andric           if (m_submenus[m_selected]->GetType() == Type::Separator)
4225480093f4SDimitry Andric             continue;
4226480093f4SDimitry Andric           else
4227480093f4SDimitry Andric             break;
4228480093f4SDimitry Andric         }
4229480093f4SDimitry Andric         return eKeyHandled;
4230480093f4SDimitry Andric       }
4231480093f4SDimitry Andric       break;
4232480093f4SDimitry Andric 
4233480093f4SDimitry Andric     case KEY_RETURN:
4234480093f4SDimitry Andric       if (static_cast<size_t>(selected_idx) < num_submenus) {
4235480093f4SDimitry Andric         if (submenus[selected_idx]->Action() == MenuActionResult::Quit)
4236480093f4SDimitry Andric           return eQuitApplication;
4237480093f4SDimitry Andric         window.GetParent()->RemoveSubWindow(&window);
4238480093f4SDimitry Andric         return eKeyHandled;
4239480093f4SDimitry Andric       }
4240480093f4SDimitry Andric       break;
4241480093f4SDimitry Andric 
4242480093f4SDimitry Andric     case KEY_ESCAPE: // Beware: pressing escape key has 1 to 2 second delay in
4243480093f4SDimitry Andric                      // case other chars are entered for escaped sequences
4244480093f4SDimitry Andric       window.GetParent()->RemoveSubWindow(&window);
4245480093f4SDimitry Andric       return eKeyHandled;
4246480093f4SDimitry Andric 
4247480093f4SDimitry Andric     default:
4248480093f4SDimitry Andric       for (size_t i = 0; i < num_submenus; ++i) {
4249480093f4SDimitry Andric         Menu *menu = submenus[i].get();
4250480093f4SDimitry Andric         if (menu->GetKeyValue() == key) {
4251480093f4SDimitry Andric           SetSelectedSubmenuIndex(i);
4252480093f4SDimitry Andric           window.GetParent()->RemoveSubWindow(&window);
4253480093f4SDimitry Andric           if (menu->Action() == MenuActionResult::Quit)
4254480093f4SDimitry Andric             return eQuitApplication;
4255480093f4SDimitry Andric           return eKeyHandled;
4256480093f4SDimitry Andric         }
4257480093f4SDimitry Andric       }
4258480093f4SDimitry Andric       break;
4259480093f4SDimitry Andric     }
4260480093f4SDimitry Andric   } else if (menu_type == Menu::Type::Separator) {
4261480093f4SDimitry Andric   }
4262480093f4SDimitry Andric   return result;
4263480093f4SDimitry Andric }
4264480093f4SDimitry Andric 
4265480093f4SDimitry Andric class Application {
4266480093f4SDimitry Andric public:
Application(FILE * in,FILE * out)426781ad6265SDimitry Andric   Application(FILE *in, FILE *out) : m_window_sp(), m_in(in), m_out(out) {}
4268480093f4SDimitry Andric 
~Application()4269480093f4SDimitry Andric   ~Application() {
4270480093f4SDimitry Andric     m_window_delegates.clear();
4271480093f4SDimitry Andric     m_window_sp.reset();
4272480093f4SDimitry Andric     if (m_screen) {
4273480093f4SDimitry Andric       ::delscreen(m_screen);
4274480093f4SDimitry Andric       m_screen = nullptr;
4275480093f4SDimitry Andric     }
4276480093f4SDimitry Andric   }
4277480093f4SDimitry Andric 
Initialize()4278480093f4SDimitry Andric   void Initialize() {
4279480093f4SDimitry Andric     m_screen = ::newterm(nullptr, m_out, m_in);
4280480093f4SDimitry Andric     ::start_color();
4281480093f4SDimitry Andric     ::curs_set(0);
4282480093f4SDimitry Andric     ::noecho();
4283480093f4SDimitry Andric     ::keypad(stdscr, TRUE);
4284480093f4SDimitry Andric   }
4285480093f4SDimitry Andric 
Terminate()4286480093f4SDimitry Andric   void Terminate() { ::endwin(); }
4287480093f4SDimitry Andric 
Run(Debugger & debugger)4288480093f4SDimitry Andric   void Run(Debugger &debugger) {
4289480093f4SDimitry Andric     bool done = false;
4290480093f4SDimitry Andric     int delay_in_tenths_of_a_second = 1;
4291480093f4SDimitry Andric 
4292349cc55cSDimitry Andric     // Alas the threading model in curses is a bit lame so we need to resort
4293349cc55cSDimitry Andric     // to polling every 0.5 seconds. We could poll for stdin ourselves and
4294349cc55cSDimitry Andric     // then pass the keys down but then we need to translate all of the escape
4295480093f4SDimitry Andric     // sequences ourselves. So we resort to polling for input because we need
4296480093f4SDimitry Andric     // to receive async process events while in this loop.
4297480093f4SDimitry Andric 
4298349cc55cSDimitry Andric     halfdelay(delay_in_tenths_of_a_second); // Poll using some number of
4299349cc55cSDimitry Andric                                             // tenths of seconds seconds when
4300349cc55cSDimitry Andric                                             // calling Window::GetChar()
4301480093f4SDimitry Andric 
4302480093f4SDimitry Andric     ListenerSP listener_sp(
4303480093f4SDimitry Andric         Listener::MakeListener("lldb.IOHandler.curses.Application"));
4304480093f4SDimitry Andric     ConstString broadcaster_class_process(Process::GetStaticBroadcasterClass());
4305480093f4SDimitry Andric     debugger.EnableForwardEvents(listener_sp);
4306480093f4SDimitry Andric 
4307e8d8bef9SDimitry Andric     m_update_screen = true;
4308480093f4SDimitry Andric #if defined(__APPLE__)
4309480093f4SDimitry Andric     std::deque<int> escape_chars;
4310480093f4SDimitry Andric #endif
4311480093f4SDimitry Andric 
4312480093f4SDimitry Andric     while (!done) {
4313e8d8bef9SDimitry Andric       if (m_update_screen) {
4314480093f4SDimitry Andric         m_window_sp->Draw(false);
4315480093f4SDimitry Andric         // All windows should be calling Window::DeferredRefresh() instead of
4316480093f4SDimitry Andric         // Window::Refresh() so we can do a single update and avoid any screen
4317480093f4SDimitry Andric         // blinking
4318480093f4SDimitry Andric         update_panels();
4319480093f4SDimitry Andric 
4320480093f4SDimitry Andric         // Cursor hiding isn't working on MacOSX, so hide it in the top left
4321480093f4SDimitry Andric         // corner
4322480093f4SDimitry Andric         m_window_sp->MoveCursor(0, 0);
4323480093f4SDimitry Andric 
4324480093f4SDimitry Andric         doupdate();
4325e8d8bef9SDimitry Andric         m_update_screen = false;
4326480093f4SDimitry Andric       }
4327480093f4SDimitry Andric 
4328480093f4SDimitry Andric #if defined(__APPLE__)
4329480093f4SDimitry Andric       // Terminal.app doesn't map its function keys correctly, F1-F4 default
4330480093f4SDimitry Andric       // to: \033OP, \033OQ, \033OR, \033OS, so lets take care of this here if
4331480093f4SDimitry Andric       // possible
4332480093f4SDimitry Andric       int ch;
4333480093f4SDimitry Andric       if (escape_chars.empty())
4334480093f4SDimitry Andric         ch = m_window_sp->GetChar();
4335480093f4SDimitry Andric       else {
4336480093f4SDimitry Andric         ch = escape_chars.front();
4337480093f4SDimitry Andric         escape_chars.pop_front();
4338480093f4SDimitry Andric       }
4339480093f4SDimitry Andric       if (ch == KEY_ESCAPE) {
4340480093f4SDimitry Andric         int ch2 = m_window_sp->GetChar();
4341480093f4SDimitry Andric         if (ch2 == 'O') {
4342480093f4SDimitry Andric           int ch3 = m_window_sp->GetChar();
4343480093f4SDimitry Andric           switch (ch3) {
4344480093f4SDimitry Andric           case 'P':
4345480093f4SDimitry Andric             ch = KEY_F(1);
4346480093f4SDimitry Andric             break;
4347480093f4SDimitry Andric           case 'Q':
4348480093f4SDimitry Andric             ch = KEY_F(2);
4349480093f4SDimitry Andric             break;
4350480093f4SDimitry Andric           case 'R':
4351480093f4SDimitry Andric             ch = KEY_F(3);
4352480093f4SDimitry Andric             break;
4353480093f4SDimitry Andric           case 'S':
4354480093f4SDimitry Andric             ch = KEY_F(4);
4355480093f4SDimitry Andric             break;
4356480093f4SDimitry Andric           default:
4357480093f4SDimitry Andric             escape_chars.push_back(ch2);
4358480093f4SDimitry Andric             if (ch3 != -1)
4359480093f4SDimitry Andric               escape_chars.push_back(ch3);
4360480093f4SDimitry Andric             break;
4361480093f4SDimitry Andric           }
4362480093f4SDimitry Andric         } else if (ch2 != -1)
4363480093f4SDimitry Andric           escape_chars.push_back(ch2);
4364480093f4SDimitry Andric       }
4365480093f4SDimitry Andric #else
4366480093f4SDimitry Andric       int ch = m_window_sp->GetChar();
4367480093f4SDimitry Andric 
4368480093f4SDimitry Andric #endif
4369480093f4SDimitry Andric       if (ch == -1) {
4370480093f4SDimitry Andric         if (feof(m_in) || ferror(m_in)) {
4371480093f4SDimitry Andric           done = true;
4372480093f4SDimitry Andric         } else {
4373480093f4SDimitry Andric           // Just a timeout from using halfdelay(), check for events
4374480093f4SDimitry Andric           EventSP event_sp;
4375480093f4SDimitry Andric           while (listener_sp->PeekAtNextEvent()) {
4376480093f4SDimitry Andric             listener_sp->GetEvent(event_sp, std::chrono::seconds(0));
4377480093f4SDimitry Andric 
4378480093f4SDimitry Andric             if (event_sp) {
4379480093f4SDimitry Andric               Broadcaster *broadcaster = event_sp->GetBroadcaster();
4380480093f4SDimitry Andric               if (broadcaster) {
4381480093f4SDimitry Andric                 // uint32_t event_type = event_sp->GetType();
4382480093f4SDimitry Andric                 ConstString broadcaster_class(
4383480093f4SDimitry Andric                     broadcaster->GetBroadcasterClass());
4384480093f4SDimitry Andric                 if (broadcaster_class == broadcaster_class_process) {
4385e8d8bef9SDimitry Andric                   m_update_screen = true;
4386480093f4SDimitry Andric                   continue; // Don't get any key, just update our view
4387480093f4SDimitry Andric                 }
4388480093f4SDimitry Andric               }
4389480093f4SDimitry Andric             }
4390480093f4SDimitry Andric           }
4391480093f4SDimitry Andric         }
4392480093f4SDimitry Andric       } else {
4393480093f4SDimitry Andric         HandleCharResult key_result = m_window_sp->HandleChar(ch);
4394480093f4SDimitry Andric         switch (key_result) {
4395480093f4SDimitry Andric         case eKeyHandled:
4396e8d8bef9SDimitry Andric           m_update_screen = true;
4397480093f4SDimitry Andric           break;
4398480093f4SDimitry Andric         case eKeyNotHandled:
4399e8d8bef9SDimitry Andric           if (ch == 12) { // Ctrl+L, force full redraw
4400e8d8bef9SDimitry Andric             redrawwin(m_window_sp->get());
4401e8d8bef9SDimitry Andric             m_update_screen = true;
4402e8d8bef9SDimitry Andric           }
4403480093f4SDimitry Andric           break;
4404480093f4SDimitry Andric         case eQuitApplication:
4405480093f4SDimitry Andric           done = true;
4406480093f4SDimitry Andric           break;
4407480093f4SDimitry Andric         }
4408480093f4SDimitry Andric       }
4409480093f4SDimitry Andric     }
4410480093f4SDimitry Andric 
4411480093f4SDimitry Andric     debugger.CancelForwardEvents(listener_sp);
4412480093f4SDimitry Andric   }
4413480093f4SDimitry Andric 
GetMainWindow()4414480093f4SDimitry Andric   WindowSP &GetMainWindow() {
4415480093f4SDimitry Andric     if (!m_window_sp)
4416480093f4SDimitry Andric       m_window_sp = std::make_shared<Window>("main", stdscr, false);
4417480093f4SDimitry Andric     return m_window_sp;
4418480093f4SDimitry Andric   }
4419480093f4SDimitry Andric 
TerminalSizeChanged()4420e8d8bef9SDimitry Andric   void TerminalSizeChanged() {
4421e8d8bef9SDimitry Andric     ::endwin();
4422e8d8bef9SDimitry Andric     ::refresh();
4423e8d8bef9SDimitry Andric     Rect content_bounds = m_window_sp->GetFrame();
4424e8d8bef9SDimitry Andric     m_window_sp->SetBounds(content_bounds);
4425e8d8bef9SDimitry Andric     if (WindowSP menubar_window_sp = m_window_sp->FindSubWindow("Menubar"))
4426e8d8bef9SDimitry Andric       menubar_window_sp->SetBounds(content_bounds.MakeMenuBar());
4427e8d8bef9SDimitry Andric     if (WindowSP status_window_sp = m_window_sp->FindSubWindow("Status"))
4428e8d8bef9SDimitry Andric       status_window_sp->SetBounds(content_bounds.MakeStatusBar());
4429e8d8bef9SDimitry Andric 
4430e8d8bef9SDimitry Andric     WindowSP source_window_sp = m_window_sp->FindSubWindow("Source");
4431e8d8bef9SDimitry Andric     WindowSP variables_window_sp = m_window_sp->FindSubWindow("Variables");
4432e8d8bef9SDimitry Andric     WindowSP registers_window_sp = m_window_sp->FindSubWindow("Registers");
4433e8d8bef9SDimitry Andric     WindowSP threads_window_sp = m_window_sp->FindSubWindow("Threads");
4434e8d8bef9SDimitry Andric 
4435e8d8bef9SDimitry Andric     Rect threads_bounds;
4436e8d8bef9SDimitry Andric     Rect source_variables_bounds;
4437e8d8bef9SDimitry Andric     content_bounds.VerticalSplitPercentage(0.80, source_variables_bounds,
4438e8d8bef9SDimitry Andric                                            threads_bounds);
4439e8d8bef9SDimitry Andric     if (threads_window_sp)
4440e8d8bef9SDimitry Andric       threads_window_sp->SetBounds(threads_bounds);
4441e8d8bef9SDimitry Andric     else
4442e8d8bef9SDimitry Andric       source_variables_bounds = content_bounds;
4443e8d8bef9SDimitry Andric 
4444e8d8bef9SDimitry Andric     Rect source_bounds;
4445e8d8bef9SDimitry Andric     Rect variables_registers_bounds;
4446e8d8bef9SDimitry Andric     source_variables_bounds.HorizontalSplitPercentage(
4447e8d8bef9SDimitry Andric         0.70, source_bounds, variables_registers_bounds);
4448e8d8bef9SDimitry Andric     if (variables_window_sp || registers_window_sp) {
4449e8d8bef9SDimitry Andric       if (variables_window_sp && registers_window_sp) {
4450e8d8bef9SDimitry Andric         Rect variables_bounds;
4451e8d8bef9SDimitry Andric         Rect registers_bounds;
4452e8d8bef9SDimitry Andric         variables_registers_bounds.VerticalSplitPercentage(
4453e8d8bef9SDimitry Andric             0.50, variables_bounds, registers_bounds);
4454e8d8bef9SDimitry Andric         variables_window_sp->SetBounds(variables_bounds);
4455e8d8bef9SDimitry Andric         registers_window_sp->SetBounds(registers_bounds);
4456e8d8bef9SDimitry Andric       } else if (variables_window_sp) {
4457e8d8bef9SDimitry Andric         variables_window_sp->SetBounds(variables_registers_bounds);
4458e8d8bef9SDimitry Andric       } else {
4459e8d8bef9SDimitry Andric         registers_window_sp->SetBounds(variables_registers_bounds);
4460e8d8bef9SDimitry Andric       }
4461e8d8bef9SDimitry Andric     } else {
4462e8d8bef9SDimitry Andric       source_bounds = source_variables_bounds;
4463e8d8bef9SDimitry Andric     }
4464e8d8bef9SDimitry Andric 
4465e8d8bef9SDimitry Andric     source_window_sp->SetBounds(source_bounds);
4466e8d8bef9SDimitry Andric 
4467e8d8bef9SDimitry Andric     touchwin(stdscr);
4468e8d8bef9SDimitry Andric     redrawwin(m_window_sp->get());
4469e8d8bef9SDimitry Andric     m_update_screen = true;
4470e8d8bef9SDimitry Andric   }
4471e8d8bef9SDimitry Andric 
4472480093f4SDimitry Andric protected:
4473480093f4SDimitry Andric   WindowSP m_window_sp;
4474480093f4SDimitry Andric   WindowDelegates m_window_delegates;
447581ad6265SDimitry Andric   SCREEN *m_screen = nullptr;
4476480093f4SDimitry Andric   FILE *m_in;
4477480093f4SDimitry Andric   FILE *m_out;
4478e8d8bef9SDimitry Andric   bool m_update_screen = false;
4479480093f4SDimitry Andric };
4480480093f4SDimitry Andric 
4481480093f4SDimitry Andric } // namespace curses
4482480093f4SDimitry Andric 
4483480093f4SDimitry Andric using namespace curses;
4484480093f4SDimitry Andric 
4485480093f4SDimitry Andric struct Row {
4486fe6060f1SDimitry Andric   ValueObjectUpdater value;
4487480093f4SDimitry Andric   Row *parent;
4488480093f4SDimitry Andric   // The process stop ID when the children were calculated.
4489e8d8bef9SDimitry Andric   uint32_t children_stop_id = 0;
4490e8d8bef9SDimitry Andric   int row_idx = 0;
4491e8d8bef9SDimitry Andric   int x = 1;
4492e8d8bef9SDimitry Andric   int y = 1;
4493480093f4SDimitry Andric   bool might_have_children;
4494e8d8bef9SDimitry Andric   bool expanded = false;
4495e8d8bef9SDimitry Andric   bool calculated_children = false;
4496480093f4SDimitry Andric   std::vector<Row> children;
4497480093f4SDimitry Andric 
RowRow4498480093f4SDimitry Andric   Row(const ValueObjectSP &v, Row *p)
4499fe6060f1SDimitry Andric       : value(v), parent(p),
4500e8d8bef9SDimitry Andric         might_have_children(v ? v->MightHaveChildren() : false) {}
4501480093f4SDimitry Andric 
GetDepthRow4502480093f4SDimitry Andric   size_t GetDepth() const {
4503480093f4SDimitry Andric     if (parent)
4504480093f4SDimitry Andric       return 1 + parent->GetDepth();
4505480093f4SDimitry Andric     return 0;
4506480093f4SDimitry Andric   }
4507480093f4SDimitry Andric 
ExpandRow4508480093f4SDimitry Andric   void Expand() { expanded = true; }
4509480093f4SDimitry Andric 
GetChildrenRow4510480093f4SDimitry Andric   std::vector<Row> &GetChildren() {
4511480093f4SDimitry Andric     ProcessSP process_sp = value.GetProcessSP();
4512480093f4SDimitry Andric     auto stop_id = process_sp->GetStopID();
4513480093f4SDimitry Andric     if (process_sp && stop_id != children_stop_id) {
4514480093f4SDimitry Andric       children_stop_id = stop_id;
4515480093f4SDimitry Andric       calculated_children = false;
4516480093f4SDimitry Andric     }
4517480093f4SDimitry Andric     if (!calculated_children) {
4518480093f4SDimitry Andric       children.clear();
4519480093f4SDimitry Andric       calculated_children = true;
4520480093f4SDimitry Andric       ValueObjectSP valobj = value.GetSP();
4521480093f4SDimitry Andric       if (valobj) {
4522480093f4SDimitry Andric         const size_t num_children = valobj->GetNumChildren();
4523480093f4SDimitry Andric         for (size_t i = 0; i < num_children; ++i) {
452406c3fb27SDimitry Andric           children.push_back(Row(valobj->GetChildAtIndex(i), this));
4525480093f4SDimitry Andric         }
4526480093f4SDimitry Andric       }
4527480093f4SDimitry Andric     }
4528480093f4SDimitry Andric     return children;
4529480093f4SDimitry Andric   }
4530480093f4SDimitry Andric 
UnexpandRow4531480093f4SDimitry Andric   void Unexpand() {
4532480093f4SDimitry Andric     expanded = false;
4533480093f4SDimitry Andric     calculated_children = false;
4534480093f4SDimitry Andric     children.clear();
4535480093f4SDimitry Andric   }
4536480093f4SDimitry Andric 
DrawTreeRow4537480093f4SDimitry Andric   void DrawTree(Window &window) {
4538480093f4SDimitry Andric     if (parent)
4539480093f4SDimitry Andric       parent->DrawTreeForChild(window, this, 0);
4540480093f4SDimitry Andric 
454181ad6265SDimitry Andric     if (might_have_children &&
454281ad6265SDimitry Andric         (!calculated_children || !GetChildren().empty())) {
4543480093f4SDimitry Andric       // It we can get UTF8 characters to work we should try to use the
4544480093f4SDimitry Andric       // "symbol" UTF8 string below
4545480093f4SDimitry Andric       //            const char *symbol = "";
4546480093f4SDimitry Andric       //            if (row.expanded)
4547480093f4SDimitry Andric       //                symbol = "\xe2\x96\xbd ";
4548480093f4SDimitry Andric       //            else
4549480093f4SDimitry Andric       //                symbol = "\xe2\x96\xb7 ";
4550480093f4SDimitry Andric       //            window.PutCString (symbol);
4551480093f4SDimitry Andric 
4552480093f4SDimitry Andric       // The ACS_DARROW and ACS_RARROW don't look very nice they are just a 'v'
4553480093f4SDimitry Andric       // or '>' character...
4554480093f4SDimitry Andric       //            if (expanded)
4555480093f4SDimitry Andric       //                window.PutChar (ACS_DARROW);
4556480093f4SDimitry Andric       //            else
4557480093f4SDimitry Andric       //                window.PutChar (ACS_RARROW);
4558480093f4SDimitry Andric       // Since we can't find any good looking right arrow/down arrow symbols,
4559480093f4SDimitry Andric       // just use a diamond...
4560480093f4SDimitry Andric       window.PutChar(ACS_DIAMOND);
4561480093f4SDimitry Andric       window.PutChar(ACS_HLINE);
4562480093f4SDimitry Andric     }
4563480093f4SDimitry Andric   }
4564480093f4SDimitry Andric 
DrawTreeForChildRow4565480093f4SDimitry Andric   void DrawTreeForChild(Window &window, Row *child, uint32_t reverse_depth) {
4566480093f4SDimitry Andric     if (parent)
4567480093f4SDimitry Andric       parent->DrawTreeForChild(window, this, reverse_depth + 1);
4568480093f4SDimitry Andric 
4569480093f4SDimitry Andric     if (&GetChildren().back() == child) {
4570480093f4SDimitry Andric       // Last child
4571480093f4SDimitry Andric       if (reverse_depth == 0) {
4572480093f4SDimitry Andric         window.PutChar(ACS_LLCORNER);
4573480093f4SDimitry Andric         window.PutChar(ACS_HLINE);
4574480093f4SDimitry Andric       } else {
4575480093f4SDimitry Andric         window.PutChar(' ');
4576480093f4SDimitry Andric         window.PutChar(' ');
4577480093f4SDimitry Andric       }
4578480093f4SDimitry Andric     } else {
4579480093f4SDimitry Andric       if (reverse_depth == 0) {
4580480093f4SDimitry Andric         window.PutChar(ACS_LTEE);
4581480093f4SDimitry Andric         window.PutChar(ACS_HLINE);
4582480093f4SDimitry Andric       } else {
4583480093f4SDimitry Andric         window.PutChar(ACS_VLINE);
4584480093f4SDimitry Andric         window.PutChar(' ');
4585480093f4SDimitry Andric       }
4586480093f4SDimitry Andric     }
4587480093f4SDimitry Andric   }
4588480093f4SDimitry Andric };
4589480093f4SDimitry Andric 
4590480093f4SDimitry Andric struct DisplayOptions {
4591480093f4SDimitry Andric   bool show_types;
4592480093f4SDimitry Andric };
4593480093f4SDimitry Andric 
4594480093f4SDimitry Andric class TreeItem;
4595480093f4SDimitry Andric 
4596480093f4SDimitry Andric class TreeDelegate {
4597480093f4SDimitry Andric public:
4598480093f4SDimitry Andric   TreeDelegate() = default;
4599480093f4SDimitry Andric   virtual ~TreeDelegate() = default;
4600480093f4SDimitry Andric 
4601480093f4SDimitry Andric   virtual void TreeDelegateDrawTreeItem(TreeItem &item, Window &window) = 0;
4602480093f4SDimitry Andric   virtual void TreeDelegateGenerateChildren(TreeItem &item) = 0;
TreeDelegateUpdateSelection(TreeItem & root,int & selection_index,TreeItem * & selected_item)4603fe6060f1SDimitry Andric   virtual void TreeDelegateUpdateSelection(TreeItem &root, int &selection_index,
46040eae32dcSDimitry Andric                                            TreeItem *&selected_item) {}
4605349cc55cSDimitry Andric   // This is invoked when a tree item is selected. If true is returned, the
4606349cc55cSDimitry Andric   // views are updated.
4607349cc55cSDimitry Andric   virtual bool TreeDelegateItemSelected(TreeItem &item) = 0;
TreeDelegateExpandRootByDefault()4608fe6060f1SDimitry Andric   virtual bool TreeDelegateExpandRootByDefault() { return false; }
4609349cc55cSDimitry Andric   // This is mostly useful for root tree delegates. If false is returned,
4610349cc55cSDimitry Andric   // drawing will be skipped completely. This is needed, for instance, in
4611349cc55cSDimitry Andric   // skipping drawing of the threads tree if there is no running process.
TreeDelegateShouldDraw()4612349cc55cSDimitry Andric   virtual bool TreeDelegateShouldDraw() { return true; }
4613480093f4SDimitry Andric };
4614480093f4SDimitry Andric 
4615480093f4SDimitry Andric typedef std::shared_ptr<TreeDelegate> TreeDelegateSP;
4616480093f4SDimitry Andric 
4617*5f757f3fSDimitry Andric struct TreeItemData {
TreeItemDataTreeItemData4618*5f757f3fSDimitry Andric   TreeItemData(TreeItem *parent, TreeDelegate &delegate,
4619*5f757f3fSDimitry Andric                bool might_have_children, bool is_expanded)
4620*5f757f3fSDimitry Andric       : m_parent(parent), m_delegate(&delegate),
4621*5f757f3fSDimitry Andric         m_might_have_children(might_have_children), m_is_expanded(is_expanded) {
4622fe6060f1SDimitry Andric   }
4623480093f4SDimitry Andric 
4624*5f757f3fSDimitry Andric protected:
4625*5f757f3fSDimitry Andric   TreeItem *m_parent;
4626*5f757f3fSDimitry Andric   TreeDelegate *m_delegate;
4627*5f757f3fSDimitry Andric   void *m_user_data = nullptr;
4628*5f757f3fSDimitry Andric   uint64_t m_identifier = 0;
4629*5f757f3fSDimitry Andric   std::string m_text;
4630*5f757f3fSDimitry Andric   int m_row_idx = -1; // Zero based visible row index, -1 if not visible or for
4631*5f757f3fSDimitry Andric                       // the root item
4632*5f757f3fSDimitry Andric   bool m_might_have_children;
4633*5f757f3fSDimitry Andric   bool m_is_expanded = false;
4634*5f757f3fSDimitry Andric };
4635*5f757f3fSDimitry Andric 
4636*5f757f3fSDimitry Andric class TreeItem : public TreeItemData {
4637*5f757f3fSDimitry Andric public:
TreeItem(TreeItem * parent,TreeDelegate & delegate,bool might_have_children)4638*5f757f3fSDimitry Andric   TreeItem(TreeItem *parent, TreeDelegate &delegate, bool might_have_children)
4639*5f757f3fSDimitry Andric       : TreeItemData(parent, delegate, might_have_children,
4640*5f757f3fSDimitry Andric                      parent == nullptr
4641*5f757f3fSDimitry Andric                          ? delegate.TreeDelegateExpandRootByDefault()
4642*5f757f3fSDimitry Andric                          : false),
4643*5f757f3fSDimitry Andric         m_children() {}
4644*5f757f3fSDimitry Andric 
4645*5f757f3fSDimitry Andric   TreeItem(const TreeItem &) = delete;
4646*5f757f3fSDimitry Andric   TreeItem &operator=(const TreeItem &rhs) = delete;
4647*5f757f3fSDimitry Andric 
operator =(TreeItem && rhs)4648*5f757f3fSDimitry Andric   TreeItem &operator=(TreeItem &&rhs) {
4649480093f4SDimitry Andric     if (this != &rhs) {
4650*5f757f3fSDimitry Andric       TreeItemData::operator=(std::move(rhs));
4651*5f757f3fSDimitry Andric       AdoptChildren(rhs.m_children);
4652480093f4SDimitry Andric     }
4653480093f4SDimitry Andric     return *this;
4654480093f4SDimitry Andric   }
4655480093f4SDimitry Andric 
TreeItem(TreeItem && rhs)4656*5f757f3fSDimitry Andric   TreeItem(TreeItem &&rhs) : TreeItemData(std::move(rhs)) {
4657*5f757f3fSDimitry Andric     AdoptChildren(rhs.m_children);
4658*5f757f3fSDimitry Andric   }
4659480093f4SDimitry Andric 
GetDepth() const4660480093f4SDimitry Andric   size_t GetDepth() const {
4661480093f4SDimitry Andric     if (m_parent)
4662480093f4SDimitry Andric       return 1 + m_parent->GetDepth();
4663480093f4SDimitry Andric     return 0;
4664480093f4SDimitry Andric   }
4665480093f4SDimitry Andric 
GetRowIndex() const4666480093f4SDimitry Andric   int GetRowIndex() const { return m_row_idx; }
4667480093f4SDimitry Andric 
ClearChildren()4668480093f4SDimitry Andric   void ClearChildren() { m_children.clear(); }
4669480093f4SDimitry Andric 
Resize(size_t n,TreeDelegate & delegate,bool might_have_children)4670*5f757f3fSDimitry Andric   void Resize(size_t n, TreeDelegate &delegate, bool might_have_children) {
4671*5f757f3fSDimitry Andric     if (m_children.size() >= n) {
4672*5f757f3fSDimitry Andric       m_children.erase(m_children.begin() + n, m_children.end());
4673*5f757f3fSDimitry Andric       return;
4674*5f757f3fSDimitry Andric     }
4675*5f757f3fSDimitry Andric     m_children.reserve(n);
4676*5f757f3fSDimitry Andric     std::generate_n(std::back_inserter(m_children), n - m_children.size(),
4677*5f757f3fSDimitry Andric                     [&, parent = this]() {
4678*5f757f3fSDimitry Andric                       return TreeItem(parent, delegate, might_have_children);
4679*5f757f3fSDimitry Andric                     });
4680*5f757f3fSDimitry Andric   }
4681480093f4SDimitry Andric 
operator [](size_t i)4682480093f4SDimitry Andric   TreeItem &operator[](size_t i) { return m_children[i]; }
4683480093f4SDimitry Andric 
SetRowIndex(int row_idx)4684480093f4SDimitry Andric   void SetRowIndex(int row_idx) { m_row_idx = row_idx; }
4685480093f4SDimitry Andric 
GetNumChildren()4686480093f4SDimitry Andric   size_t GetNumChildren() {
4687*5f757f3fSDimitry Andric     m_delegate->TreeDelegateGenerateChildren(*this);
4688480093f4SDimitry Andric     return m_children.size();
4689480093f4SDimitry Andric   }
4690480093f4SDimitry Andric 
ItemWasSelected()4691*5f757f3fSDimitry Andric   void ItemWasSelected() { m_delegate->TreeDelegateItemSelected(*this); }
4692480093f4SDimitry Andric 
CalculateRowIndexes(int & row_idx)4693480093f4SDimitry Andric   void CalculateRowIndexes(int &row_idx) {
4694480093f4SDimitry Andric     SetRowIndex(row_idx);
4695480093f4SDimitry Andric     ++row_idx;
4696480093f4SDimitry Andric 
4697480093f4SDimitry Andric     const bool expanded = IsExpanded();
4698480093f4SDimitry Andric 
4699480093f4SDimitry Andric     // The root item must calculate its children, or we must calculate the
4700480093f4SDimitry Andric     // number of children if the item is expanded
4701480093f4SDimitry Andric     if (m_parent == nullptr || expanded)
4702480093f4SDimitry Andric       GetNumChildren();
4703480093f4SDimitry Andric 
4704480093f4SDimitry Andric     for (auto &item : m_children) {
4705480093f4SDimitry Andric       if (expanded)
4706480093f4SDimitry Andric         item.CalculateRowIndexes(row_idx);
4707480093f4SDimitry Andric       else
4708480093f4SDimitry Andric         item.SetRowIndex(-1);
4709480093f4SDimitry Andric     }
4710480093f4SDimitry Andric   }
4711480093f4SDimitry Andric 
GetParent()4712480093f4SDimitry Andric   TreeItem *GetParent() { return m_parent; }
4713480093f4SDimitry Andric 
IsExpanded() const4714480093f4SDimitry Andric   bool IsExpanded() const { return m_is_expanded; }
4715480093f4SDimitry Andric 
Expand()4716480093f4SDimitry Andric   void Expand() { m_is_expanded = true; }
4717480093f4SDimitry Andric 
Unexpand()4718480093f4SDimitry Andric   void Unexpand() { m_is_expanded = false; }
4719480093f4SDimitry Andric 
Draw(Window & window,const int first_visible_row,const uint32_t selected_row_idx,int & row_idx,int & num_rows_left)4720480093f4SDimitry Andric   bool Draw(Window &window, const int first_visible_row,
4721480093f4SDimitry Andric             const uint32_t selected_row_idx, int &row_idx, int &num_rows_left) {
4722480093f4SDimitry Andric     if (num_rows_left <= 0)
4723480093f4SDimitry Andric       return false;
4724480093f4SDimitry Andric 
4725480093f4SDimitry Andric     if (m_row_idx >= first_visible_row) {
4726480093f4SDimitry Andric       window.MoveCursor(2, row_idx + 1);
4727480093f4SDimitry Andric 
4728480093f4SDimitry Andric       if (m_parent)
4729480093f4SDimitry Andric         m_parent->DrawTreeForChild(window, this, 0);
4730480093f4SDimitry Andric 
4731480093f4SDimitry Andric       if (m_might_have_children) {
4732480093f4SDimitry Andric         // It we can get UTF8 characters to work we should try to use the
4733480093f4SDimitry Andric         // "symbol" UTF8 string below
4734480093f4SDimitry Andric         //            const char *symbol = "";
4735480093f4SDimitry Andric         //            if (row.expanded)
4736480093f4SDimitry Andric         //                symbol = "\xe2\x96\xbd ";
4737480093f4SDimitry Andric         //            else
4738480093f4SDimitry Andric         //                symbol = "\xe2\x96\xb7 ";
4739480093f4SDimitry Andric         //            window.PutCString (symbol);
4740480093f4SDimitry Andric 
4741480093f4SDimitry Andric         // The ACS_DARROW and ACS_RARROW don't look very nice they are just a
4742480093f4SDimitry Andric         // 'v' or '>' character...
4743480093f4SDimitry Andric         //            if (expanded)
4744480093f4SDimitry Andric         //                window.PutChar (ACS_DARROW);
4745480093f4SDimitry Andric         //            else
4746480093f4SDimitry Andric         //                window.PutChar (ACS_RARROW);
4747480093f4SDimitry Andric         // Since we can't find any good looking right arrow/down arrow symbols,
4748480093f4SDimitry Andric         // just use a diamond...
4749480093f4SDimitry Andric         window.PutChar(ACS_DIAMOND);
4750480093f4SDimitry Andric         window.PutChar(ACS_HLINE);
4751480093f4SDimitry Andric       }
4752480093f4SDimitry Andric       bool highlight = (selected_row_idx == static_cast<size_t>(m_row_idx)) &&
4753480093f4SDimitry Andric                        window.IsActive();
4754480093f4SDimitry Andric 
4755480093f4SDimitry Andric       if (highlight)
4756480093f4SDimitry Andric         window.AttributeOn(A_REVERSE);
4757480093f4SDimitry Andric 
4758*5f757f3fSDimitry Andric       m_delegate->TreeDelegateDrawTreeItem(*this, window);
4759480093f4SDimitry Andric 
4760480093f4SDimitry Andric       if (highlight)
4761480093f4SDimitry Andric         window.AttributeOff(A_REVERSE);
4762480093f4SDimitry Andric       ++row_idx;
4763480093f4SDimitry Andric       --num_rows_left;
4764480093f4SDimitry Andric     }
4765480093f4SDimitry Andric 
4766480093f4SDimitry Andric     if (num_rows_left <= 0)
4767480093f4SDimitry Andric       return false; // We are done drawing...
4768480093f4SDimitry Andric 
4769480093f4SDimitry Andric     if (IsExpanded()) {
4770480093f4SDimitry Andric       for (auto &item : m_children) {
4771480093f4SDimitry Andric         // If we displayed all the rows and item.Draw() returns false we are
4772480093f4SDimitry Andric         // done drawing and can exit this for loop
4773480093f4SDimitry Andric         if (!item.Draw(window, first_visible_row, selected_row_idx, row_idx,
4774480093f4SDimitry Andric                        num_rows_left))
4775480093f4SDimitry Andric           break;
4776480093f4SDimitry Andric       }
4777480093f4SDimitry Andric     }
4778480093f4SDimitry Andric     return num_rows_left >= 0; // Return true if not done drawing yet
4779480093f4SDimitry Andric   }
4780480093f4SDimitry Andric 
DrawTreeForChild(Window & window,TreeItem * child,uint32_t reverse_depth)4781480093f4SDimitry Andric   void DrawTreeForChild(Window &window, TreeItem *child,
4782480093f4SDimitry Andric                         uint32_t reverse_depth) {
4783480093f4SDimitry Andric     if (m_parent)
4784480093f4SDimitry Andric       m_parent->DrawTreeForChild(window, this, reverse_depth + 1);
4785480093f4SDimitry Andric 
4786480093f4SDimitry Andric     if (&m_children.back() == child) {
4787480093f4SDimitry Andric       // Last child
4788480093f4SDimitry Andric       if (reverse_depth == 0) {
4789480093f4SDimitry Andric         window.PutChar(ACS_LLCORNER);
4790480093f4SDimitry Andric         window.PutChar(ACS_HLINE);
4791480093f4SDimitry Andric       } else {
4792480093f4SDimitry Andric         window.PutChar(' ');
4793480093f4SDimitry Andric         window.PutChar(' ');
4794480093f4SDimitry Andric       }
4795480093f4SDimitry Andric     } else {
4796480093f4SDimitry Andric       if (reverse_depth == 0) {
4797480093f4SDimitry Andric         window.PutChar(ACS_LTEE);
4798480093f4SDimitry Andric         window.PutChar(ACS_HLINE);
4799480093f4SDimitry Andric       } else {
4800480093f4SDimitry Andric         window.PutChar(ACS_VLINE);
4801480093f4SDimitry Andric         window.PutChar(' ');
4802480093f4SDimitry Andric       }
4803480093f4SDimitry Andric     }
4804480093f4SDimitry Andric   }
4805480093f4SDimitry Andric 
GetItemForRowIndex(uint32_t row_idx)4806480093f4SDimitry Andric   TreeItem *GetItemForRowIndex(uint32_t row_idx) {
4807480093f4SDimitry Andric     if (static_cast<uint32_t>(m_row_idx) == row_idx)
4808480093f4SDimitry Andric       return this;
4809480093f4SDimitry Andric     if (m_children.empty())
4810480093f4SDimitry Andric       return nullptr;
4811480093f4SDimitry Andric     if (IsExpanded()) {
4812480093f4SDimitry Andric       for (auto &item : m_children) {
4813480093f4SDimitry Andric         TreeItem *selected_item_ptr = item.GetItemForRowIndex(row_idx);
4814480093f4SDimitry Andric         if (selected_item_ptr)
4815480093f4SDimitry Andric           return selected_item_ptr;
4816480093f4SDimitry Andric       }
4817480093f4SDimitry Andric     }
4818480093f4SDimitry Andric     return nullptr;
4819480093f4SDimitry Andric   }
4820480093f4SDimitry Andric 
GetUserData() const4821480093f4SDimitry Andric   void *GetUserData() const { return m_user_data; }
4822480093f4SDimitry Andric 
SetUserData(void * user_data)4823480093f4SDimitry Andric   void SetUserData(void *user_data) { m_user_data = user_data; }
4824480093f4SDimitry Andric 
GetIdentifier() const4825480093f4SDimitry Andric   uint64_t GetIdentifier() const { return m_identifier; }
4826480093f4SDimitry Andric 
SetIdentifier(uint64_t identifier)4827480093f4SDimitry Andric   void SetIdentifier(uint64_t identifier) { m_identifier = identifier; }
4828480093f4SDimitry Andric 
GetText() const4829349cc55cSDimitry Andric   const std::string &GetText() const { return m_text; }
4830349cc55cSDimitry Andric 
SetText(const char * text)4831349cc55cSDimitry Andric   void SetText(const char *text) {
4832349cc55cSDimitry Andric     if (text == nullptr) {
4833349cc55cSDimitry Andric       m_text.clear();
4834349cc55cSDimitry Andric       return;
4835349cc55cSDimitry Andric     }
4836349cc55cSDimitry Andric     m_text = text;
4837349cc55cSDimitry Andric   }
4838349cc55cSDimitry Andric 
SetMightHaveChildren(bool b)4839480093f4SDimitry Andric   void SetMightHaveChildren(bool b) { m_might_have_children = b; }
4840480093f4SDimitry Andric 
4841480093f4SDimitry Andric protected:
AdoptChildren(std::vector<TreeItem> & children)4842*5f757f3fSDimitry Andric   void AdoptChildren(std::vector<TreeItem> &children) {
4843*5f757f3fSDimitry Andric     m_children = std::move(children);
4844*5f757f3fSDimitry Andric     for (auto &child : m_children)
4845*5f757f3fSDimitry Andric       child.m_parent = this;
4846*5f757f3fSDimitry Andric   }
4847*5f757f3fSDimitry Andric 
4848480093f4SDimitry Andric   std::vector<TreeItem> m_children;
4849480093f4SDimitry Andric };
4850480093f4SDimitry Andric 
4851480093f4SDimitry Andric class TreeWindowDelegate : public WindowDelegate {
4852480093f4SDimitry Andric public:
TreeWindowDelegate(Debugger & debugger,const TreeDelegateSP & delegate_sp)4853480093f4SDimitry Andric   TreeWindowDelegate(Debugger &debugger, const TreeDelegateSP &delegate_sp)
4854480093f4SDimitry Andric       : m_debugger(debugger), m_delegate_sp(delegate_sp),
485581ad6265SDimitry Andric         m_root(nullptr, *delegate_sp, true) {}
4856480093f4SDimitry Andric 
NumVisibleRows() const4857480093f4SDimitry Andric   int NumVisibleRows() const { return m_max_y - m_min_y; }
4858480093f4SDimitry Andric 
WindowDelegateDraw(Window & window,bool force)4859480093f4SDimitry Andric   bool WindowDelegateDraw(Window &window, bool force) override {
4860480093f4SDimitry Andric     m_min_x = 2;
4861480093f4SDimitry Andric     m_min_y = 1;
4862480093f4SDimitry Andric     m_max_x = window.GetWidth() - 1;
4863480093f4SDimitry Andric     m_max_y = window.GetHeight() - 1;
4864480093f4SDimitry Andric 
4865480093f4SDimitry Andric     window.Erase();
4866480093f4SDimitry Andric     window.DrawTitleBox(window.GetName());
4867480093f4SDimitry Andric 
4868349cc55cSDimitry Andric     if (!m_delegate_sp->TreeDelegateShouldDraw()) {
4869349cc55cSDimitry Andric       m_selected_item = nullptr;
4870349cc55cSDimitry Andric       return true;
4871349cc55cSDimitry Andric     }
4872349cc55cSDimitry Andric 
4873480093f4SDimitry Andric     const int num_visible_rows = NumVisibleRows();
4874480093f4SDimitry Andric     m_num_rows = 0;
4875480093f4SDimitry Andric     m_root.CalculateRowIndexes(m_num_rows);
4876fe6060f1SDimitry Andric     m_delegate_sp->TreeDelegateUpdateSelection(m_root, m_selected_row_idx,
4877fe6060f1SDimitry Andric                                                m_selected_item);
4878480093f4SDimitry Andric 
4879480093f4SDimitry Andric     // If we unexpanded while having something selected our total number of
4880480093f4SDimitry Andric     // rows is less than the num visible rows, then make sure we show all the
4881480093f4SDimitry Andric     // rows by setting the first visible row accordingly.
4882480093f4SDimitry Andric     if (m_first_visible_row > 0 && m_num_rows < num_visible_rows)
4883480093f4SDimitry Andric       m_first_visible_row = 0;
4884480093f4SDimitry Andric 
4885480093f4SDimitry Andric     // Make sure the selected row is always visible
4886480093f4SDimitry Andric     if (m_selected_row_idx < m_first_visible_row)
4887480093f4SDimitry Andric       m_first_visible_row = m_selected_row_idx;
4888480093f4SDimitry Andric     else if (m_first_visible_row + num_visible_rows <= m_selected_row_idx)
4889480093f4SDimitry Andric       m_first_visible_row = m_selected_row_idx - num_visible_rows + 1;
4890480093f4SDimitry Andric 
4891480093f4SDimitry Andric     int row_idx = 0;
4892480093f4SDimitry Andric     int num_rows_left = num_visible_rows;
4893480093f4SDimitry Andric     m_root.Draw(window, m_first_visible_row, m_selected_row_idx, row_idx,
4894480093f4SDimitry Andric                 num_rows_left);
4895480093f4SDimitry Andric     // Get the selected row
4896480093f4SDimitry Andric     m_selected_item = m_root.GetItemForRowIndex(m_selected_row_idx);
4897480093f4SDimitry Andric 
4898480093f4SDimitry Andric     return true; // Drawing handled
4899480093f4SDimitry Andric   }
4900480093f4SDimitry Andric 
WindowDelegateGetHelpText()4901480093f4SDimitry Andric   const char *WindowDelegateGetHelpText() override {
4902480093f4SDimitry Andric     return "Thread window keyboard shortcuts:";
4903480093f4SDimitry Andric   }
4904480093f4SDimitry Andric 
WindowDelegateGetKeyHelp()4905480093f4SDimitry Andric   KeyHelp *WindowDelegateGetKeyHelp() override {
4906480093f4SDimitry Andric     static curses::KeyHelp g_source_view_key_help[] = {
4907480093f4SDimitry Andric         {KEY_UP, "Select previous item"},
4908480093f4SDimitry Andric         {KEY_DOWN, "Select next item"},
4909480093f4SDimitry Andric         {KEY_RIGHT, "Expand the selected item"},
4910480093f4SDimitry Andric         {KEY_LEFT,
4911480093f4SDimitry Andric          "Unexpand the selected item or select parent if not expanded"},
4912480093f4SDimitry Andric         {KEY_PPAGE, "Page up"},
4913480093f4SDimitry Andric         {KEY_NPAGE, "Page down"},
4914480093f4SDimitry Andric         {'h', "Show help dialog"},
4915480093f4SDimitry Andric         {' ', "Toggle item expansion"},
4916480093f4SDimitry Andric         {',', "Page up"},
4917480093f4SDimitry Andric         {'.', "Page down"},
4918480093f4SDimitry Andric         {'\0', nullptr}};
4919480093f4SDimitry Andric     return g_source_view_key_help;
4920480093f4SDimitry Andric   }
4921480093f4SDimitry Andric 
WindowDelegateHandleChar(Window & window,int c)4922480093f4SDimitry Andric   HandleCharResult WindowDelegateHandleChar(Window &window, int c) override {
4923480093f4SDimitry Andric     switch (c) {
4924480093f4SDimitry Andric     case ',':
4925480093f4SDimitry Andric     case KEY_PPAGE:
4926480093f4SDimitry Andric       // Page up key
4927480093f4SDimitry Andric       if (m_first_visible_row > 0) {
4928480093f4SDimitry Andric         if (m_first_visible_row > m_max_y)
4929480093f4SDimitry Andric           m_first_visible_row -= m_max_y;
4930480093f4SDimitry Andric         else
4931480093f4SDimitry Andric           m_first_visible_row = 0;
4932480093f4SDimitry Andric         m_selected_row_idx = m_first_visible_row;
4933480093f4SDimitry Andric         m_selected_item = m_root.GetItemForRowIndex(m_selected_row_idx);
4934480093f4SDimitry Andric         if (m_selected_item)
4935480093f4SDimitry Andric           m_selected_item->ItemWasSelected();
4936480093f4SDimitry Andric       }
4937480093f4SDimitry Andric       return eKeyHandled;
4938480093f4SDimitry Andric 
4939480093f4SDimitry Andric     case '.':
4940480093f4SDimitry Andric     case KEY_NPAGE:
4941480093f4SDimitry Andric       // Page down key
4942480093f4SDimitry Andric       if (m_num_rows > m_max_y) {
4943480093f4SDimitry Andric         if (m_first_visible_row + m_max_y < m_num_rows) {
4944480093f4SDimitry Andric           m_first_visible_row += m_max_y;
4945480093f4SDimitry Andric           m_selected_row_idx = m_first_visible_row;
4946480093f4SDimitry Andric           m_selected_item = m_root.GetItemForRowIndex(m_selected_row_idx);
4947480093f4SDimitry Andric           if (m_selected_item)
4948480093f4SDimitry Andric             m_selected_item->ItemWasSelected();
4949480093f4SDimitry Andric         }
4950480093f4SDimitry Andric       }
4951480093f4SDimitry Andric       return eKeyHandled;
4952480093f4SDimitry Andric 
4953480093f4SDimitry Andric     case KEY_UP:
4954480093f4SDimitry Andric       if (m_selected_row_idx > 0) {
4955480093f4SDimitry Andric         --m_selected_row_idx;
4956480093f4SDimitry Andric         m_selected_item = m_root.GetItemForRowIndex(m_selected_row_idx);
4957480093f4SDimitry Andric         if (m_selected_item)
4958480093f4SDimitry Andric           m_selected_item->ItemWasSelected();
4959480093f4SDimitry Andric       }
4960480093f4SDimitry Andric       return eKeyHandled;
4961480093f4SDimitry Andric 
4962480093f4SDimitry Andric     case KEY_DOWN:
4963480093f4SDimitry Andric       if (m_selected_row_idx + 1 < m_num_rows) {
4964480093f4SDimitry Andric         ++m_selected_row_idx;
4965480093f4SDimitry Andric         m_selected_item = m_root.GetItemForRowIndex(m_selected_row_idx);
4966480093f4SDimitry Andric         if (m_selected_item)
4967480093f4SDimitry Andric           m_selected_item->ItemWasSelected();
4968480093f4SDimitry Andric       }
4969480093f4SDimitry Andric       return eKeyHandled;
4970480093f4SDimitry Andric 
4971480093f4SDimitry Andric     case KEY_RIGHT:
4972480093f4SDimitry Andric       if (m_selected_item) {
4973480093f4SDimitry Andric         if (!m_selected_item->IsExpanded())
4974480093f4SDimitry Andric           m_selected_item->Expand();
4975480093f4SDimitry Andric       }
4976480093f4SDimitry Andric       return eKeyHandled;
4977480093f4SDimitry Andric 
4978480093f4SDimitry Andric     case KEY_LEFT:
4979480093f4SDimitry Andric       if (m_selected_item) {
4980480093f4SDimitry Andric         if (m_selected_item->IsExpanded())
4981480093f4SDimitry Andric           m_selected_item->Unexpand();
4982480093f4SDimitry Andric         else if (m_selected_item->GetParent()) {
4983480093f4SDimitry Andric           m_selected_row_idx = m_selected_item->GetParent()->GetRowIndex();
4984480093f4SDimitry Andric           m_selected_item = m_root.GetItemForRowIndex(m_selected_row_idx);
4985480093f4SDimitry Andric           if (m_selected_item)
4986480093f4SDimitry Andric             m_selected_item->ItemWasSelected();
4987480093f4SDimitry Andric         }
4988480093f4SDimitry Andric       }
4989480093f4SDimitry Andric       return eKeyHandled;
4990480093f4SDimitry Andric 
4991480093f4SDimitry Andric     case ' ':
4992480093f4SDimitry Andric       // Toggle expansion state when SPACE is pressed
4993480093f4SDimitry Andric       if (m_selected_item) {
4994480093f4SDimitry Andric         if (m_selected_item->IsExpanded())
4995480093f4SDimitry Andric           m_selected_item->Unexpand();
4996480093f4SDimitry Andric         else
4997480093f4SDimitry Andric           m_selected_item->Expand();
4998480093f4SDimitry Andric       }
4999480093f4SDimitry Andric       return eKeyHandled;
5000480093f4SDimitry Andric 
5001480093f4SDimitry Andric     case 'h':
5002480093f4SDimitry Andric       window.CreateHelpSubwindow();
5003480093f4SDimitry Andric       return eKeyHandled;
5004480093f4SDimitry Andric 
5005480093f4SDimitry Andric     default:
5006480093f4SDimitry Andric       break;
5007480093f4SDimitry Andric     }
5008480093f4SDimitry Andric     return eKeyNotHandled;
5009480093f4SDimitry Andric   }
5010480093f4SDimitry Andric 
5011480093f4SDimitry Andric protected:
5012480093f4SDimitry Andric   Debugger &m_debugger;
5013480093f4SDimitry Andric   TreeDelegateSP m_delegate_sp;
5014480093f4SDimitry Andric   TreeItem m_root;
501581ad6265SDimitry Andric   TreeItem *m_selected_item = nullptr;
501681ad6265SDimitry Andric   int m_num_rows = 0;
501781ad6265SDimitry Andric   int m_selected_row_idx = 0;
501881ad6265SDimitry Andric   int m_first_visible_row = 0;
501981ad6265SDimitry Andric   int m_min_x = 0;
502081ad6265SDimitry Andric   int m_min_y = 0;
502181ad6265SDimitry Andric   int m_max_x = 0;
502281ad6265SDimitry Andric   int m_max_y = 0;
5023480093f4SDimitry Andric };
5024480093f4SDimitry Andric 
5025349cc55cSDimitry Andric // A tree delegate that just draws the text member of the tree item, it doesn't
5026349cc55cSDimitry Andric // have any children or actions.
5027349cc55cSDimitry Andric class TextTreeDelegate : public TreeDelegate {
5028349cc55cSDimitry Andric public:
TextTreeDelegate()5029349cc55cSDimitry Andric   TextTreeDelegate() : TreeDelegate() {}
5030349cc55cSDimitry Andric 
5031349cc55cSDimitry Andric   ~TextTreeDelegate() override = default;
5032349cc55cSDimitry Andric 
TreeDelegateDrawTreeItem(TreeItem & item,Window & window)5033349cc55cSDimitry Andric   void TreeDelegateDrawTreeItem(TreeItem &item, Window &window) override {
5034349cc55cSDimitry Andric     window.PutCStringTruncated(1, item.GetText().c_str());
5035349cc55cSDimitry Andric   }
5036349cc55cSDimitry Andric 
TreeDelegateGenerateChildren(TreeItem & item)5037349cc55cSDimitry Andric   void TreeDelegateGenerateChildren(TreeItem &item) override {}
5038349cc55cSDimitry Andric 
TreeDelegateItemSelected(TreeItem & item)5039349cc55cSDimitry Andric   bool TreeDelegateItemSelected(TreeItem &item) override { return false; }
5040349cc55cSDimitry Andric };
5041349cc55cSDimitry Andric 
5042480093f4SDimitry Andric class FrameTreeDelegate : public TreeDelegate {
5043480093f4SDimitry Andric public:
FrameTreeDelegate()5044480093f4SDimitry Andric   FrameTreeDelegate() : TreeDelegate() {
5045480093f4SDimitry Andric     FormatEntity::Parse(
504681ad6265SDimitry Andric         "#${frame.index}: {${function.name}${function.pc-offset}}}", m_format);
5047480093f4SDimitry Andric   }
5048480093f4SDimitry Andric 
5049480093f4SDimitry Andric   ~FrameTreeDelegate() override = default;
5050480093f4SDimitry Andric 
TreeDelegateDrawTreeItem(TreeItem & item,Window & window)5051480093f4SDimitry Andric   void TreeDelegateDrawTreeItem(TreeItem &item, Window &window) override {
5052480093f4SDimitry Andric     Thread *thread = (Thread *)item.GetUserData();
5053480093f4SDimitry Andric     if (thread) {
5054480093f4SDimitry Andric       const uint64_t frame_idx = item.GetIdentifier();
5055480093f4SDimitry Andric       StackFrameSP frame_sp = thread->GetStackFrameAtIndex(frame_idx);
5056480093f4SDimitry Andric       if (frame_sp) {
5057480093f4SDimitry Andric         StreamString strm;
5058480093f4SDimitry Andric         const SymbolContext &sc =
5059480093f4SDimitry Andric             frame_sp->GetSymbolContext(eSymbolContextEverything);
5060480093f4SDimitry Andric         ExecutionContext exe_ctx(frame_sp);
5061480093f4SDimitry Andric         if (FormatEntity::Format(m_format, strm, &sc, &exe_ctx, nullptr,
5062480093f4SDimitry Andric                                  nullptr, false, false)) {
5063480093f4SDimitry Andric           int right_pad = 1;
5064e8d8bef9SDimitry Andric           window.PutCStringTruncated(right_pad, strm.GetString().str().c_str());
5065480093f4SDimitry Andric         }
5066480093f4SDimitry Andric       }
5067480093f4SDimitry Andric     }
5068480093f4SDimitry Andric   }
5069480093f4SDimitry Andric 
TreeDelegateGenerateChildren(TreeItem & item)5070480093f4SDimitry Andric   void TreeDelegateGenerateChildren(TreeItem &item) override {
5071480093f4SDimitry Andric     // No children for frames yet...
5072480093f4SDimitry Andric   }
5073480093f4SDimitry Andric 
TreeDelegateItemSelected(TreeItem & item)5074480093f4SDimitry Andric   bool TreeDelegateItemSelected(TreeItem &item) override {
5075480093f4SDimitry Andric     Thread *thread = (Thread *)item.GetUserData();
5076480093f4SDimitry Andric     if (thread) {
5077480093f4SDimitry Andric       thread->GetProcess()->GetThreadList().SetSelectedThreadByID(
5078480093f4SDimitry Andric           thread->GetID());
5079480093f4SDimitry Andric       const uint64_t frame_idx = item.GetIdentifier();
5080480093f4SDimitry Andric       thread->SetSelectedFrameByIndex(frame_idx);
5081480093f4SDimitry Andric       return true;
5082480093f4SDimitry Andric     }
5083480093f4SDimitry Andric     return false;
5084480093f4SDimitry Andric   }
5085480093f4SDimitry Andric 
5086480093f4SDimitry Andric protected:
5087480093f4SDimitry Andric   FormatEntity::Entry m_format;
5088480093f4SDimitry Andric };
5089480093f4SDimitry Andric 
5090480093f4SDimitry Andric class ThreadTreeDelegate : public TreeDelegate {
5091480093f4SDimitry Andric public:
ThreadTreeDelegate(Debugger & debugger)5092480093f4SDimitry Andric   ThreadTreeDelegate(Debugger &debugger)
509381ad6265SDimitry Andric       : TreeDelegate(), m_debugger(debugger) {
5094480093f4SDimitry Andric     FormatEntity::Parse("thread #${thread.index}: tid = ${thread.id}{, stop "
5095480093f4SDimitry Andric                         "reason = ${thread.stop-reason}}",
5096480093f4SDimitry Andric                         m_format);
5097480093f4SDimitry Andric   }
5098480093f4SDimitry Andric 
5099480093f4SDimitry Andric   ~ThreadTreeDelegate() override = default;
5100480093f4SDimitry Andric 
GetProcess()5101480093f4SDimitry Andric   ProcessSP GetProcess() {
5102480093f4SDimitry Andric     return m_debugger.GetCommandInterpreter()
5103480093f4SDimitry Andric         .GetExecutionContext()
5104480093f4SDimitry Andric         .GetProcessSP();
5105480093f4SDimitry Andric   }
5106480093f4SDimitry Andric 
GetThread(const TreeItem & item)5107480093f4SDimitry Andric   ThreadSP GetThread(const TreeItem &item) {
5108480093f4SDimitry Andric     ProcessSP process_sp = GetProcess();
5109480093f4SDimitry Andric     if (process_sp)
5110480093f4SDimitry Andric       return process_sp->GetThreadList().FindThreadByID(item.GetIdentifier());
5111480093f4SDimitry Andric     return ThreadSP();
5112480093f4SDimitry Andric   }
5113480093f4SDimitry Andric 
TreeDelegateDrawTreeItem(TreeItem & item,Window & window)5114480093f4SDimitry Andric   void TreeDelegateDrawTreeItem(TreeItem &item, Window &window) override {
5115480093f4SDimitry Andric     ThreadSP thread_sp = GetThread(item);
5116480093f4SDimitry Andric     if (thread_sp) {
5117480093f4SDimitry Andric       StreamString strm;
5118480093f4SDimitry Andric       ExecutionContext exe_ctx(thread_sp);
5119480093f4SDimitry Andric       if (FormatEntity::Format(m_format, strm, nullptr, &exe_ctx, nullptr,
5120480093f4SDimitry Andric                                nullptr, false, false)) {
5121480093f4SDimitry Andric         int right_pad = 1;
5122e8d8bef9SDimitry Andric         window.PutCStringTruncated(right_pad, strm.GetString().str().c_str());
5123480093f4SDimitry Andric       }
5124480093f4SDimitry Andric     }
5125480093f4SDimitry Andric   }
5126480093f4SDimitry Andric 
TreeDelegateGenerateChildren(TreeItem & item)5127480093f4SDimitry Andric   void TreeDelegateGenerateChildren(TreeItem &item) override {
5128480093f4SDimitry Andric     ProcessSP process_sp = GetProcess();
5129480093f4SDimitry Andric     if (process_sp && process_sp->IsAlive()) {
5130480093f4SDimitry Andric       StateType state = process_sp->GetState();
5131480093f4SDimitry Andric       if (StateIsStoppedState(state, true)) {
5132480093f4SDimitry Andric         ThreadSP thread_sp = GetThread(item);
5133480093f4SDimitry Andric         if (thread_sp) {
5134480093f4SDimitry Andric           if (m_stop_id == process_sp->GetStopID() &&
5135480093f4SDimitry Andric               thread_sp->GetID() == m_tid)
5136480093f4SDimitry Andric             return; // Children are already up to date
5137480093f4SDimitry Andric           if (!m_frame_delegate_sp) {
5138480093f4SDimitry Andric             // Always expand the thread item the first time we show it
5139480093f4SDimitry Andric             m_frame_delegate_sp = std::make_shared<FrameTreeDelegate>();
5140480093f4SDimitry Andric           }
5141480093f4SDimitry Andric 
5142480093f4SDimitry Andric           m_stop_id = process_sp->GetStopID();
5143480093f4SDimitry Andric           m_tid = thread_sp->GetID();
5144480093f4SDimitry Andric 
5145480093f4SDimitry Andric           size_t num_frames = thread_sp->GetStackFrameCount();
5146*5f757f3fSDimitry Andric           item.Resize(num_frames, *m_frame_delegate_sp, false);
5147480093f4SDimitry Andric           for (size_t i = 0; i < num_frames; ++i) {
5148480093f4SDimitry Andric             item[i].SetUserData(thread_sp.get());
5149480093f4SDimitry Andric             item[i].SetIdentifier(i);
5150480093f4SDimitry Andric           }
5151480093f4SDimitry Andric         }
5152480093f4SDimitry Andric         return;
5153480093f4SDimitry Andric       }
5154480093f4SDimitry Andric     }
5155480093f4SDimitry Andric     item.ClearChildren();
5156480093f4SDimitry Andric   }
5157480093f4SDimitry Andric 
TreeDelegateItemSelected(TreeItem & item)5158480093f4SDimitry Andric   bool TreeDelegateItemSelected(TreeItem &item) override {
5159480093f4SDimitry Andric     ProcessSP process_sp = GetProcess();
5160480093f4SDimitry Andric     if (process_sp && process_sp->IsAlive()) {
5161480093f4SDimitry Andric       StateType state = process_sp->GetState();
5162480093f4SDimitry Andric       if (StateIsStoppedState(state, true)) {
5163480093f4SDimitry Andric         ThreadSP thread_sp = GetThread(item);
5164480093f4SDimitry Andric         if (thread_sp) {
5165480093f4SDimitry Andric           ThreadList &thread_list = thread_sp->GetProcess()->GetThreadList();
5166480093f4SDimitry Andric           std::lock_guard<std::recursive_mutex> guard(thread_list.GetMutex());
5167480093f4SDimitry Andric           ThreadSP selected_thread_sp = thread_list.GetSelectedThread();
5168480093f4SDimitry Andric           if (selected_thread_sp->GetID() != thread_sp->GetID()) {
5169480093f4SDimitry Andric             thread_list.SetSelectedThreadByID(thread_sp->GetID());
5170480093f4SDimitry Andric             return true;
5171480093f4SDimitry Andric           }
5172480093f4SDimitry Andric         }
5173480093f4SDimitry Andric       }
5174480093f4SDimitry Andric     }
5175480093f4SDimitry Andric     return false;
5176480093f4SDimitry Andric   }
5177480093f4SDimitry Andric 
5178480093f4SDimitry Andric protected:
5179480093f4SDimitry Andric   Debugger &m_debugger;
5180480093f4SDimitry Andric   std::shared_ptr<FrameTreeDelegate> m_frame_delegate_sp;
518181ad6265SDimitry Andric   lldb::user_id_t m_tid = LLDB_INVALID_THREAD_ID;
518281ad6265SDimitry Andric   uint32_t m_stop_id = UINT32_MAX;
5183480093f4SDimitry Andric   FormatEntity::Entry m_format;
5184480093f4SDimitry Andric };
5185480093f4SDimitry Andric 
5186480093f4SDimitry Andric class ThreadsTreeDelegate : public TreeDelegate {
5187480093f4SDimitry Andric public:
ThreadsTreeDelegate(Debugger & debugger)5188480093f4SDimitry Andric   ThreadsTreeDelegate(Debugger &debugger)
518981ad6265SDimitry Andric       : TreeDelegate(), m_thread_delegate_sp(), m_debugger(debugger) {
5190480093f4SDimitry Andric     FormatEntity::Parse("process ${process.id}{, name = ${process.name}}",
5191480093f4SDimitry Andric                         m_format);
5192480093f4SDimitry Andric   }
5193480093f4SDimitry Andric 
5194480093f4SDimitry Andric   ~ThreadsTreeDelegate() override = default;
5195480093f4SDimitry Andric 
GetProcess()5196480093f4SDimitry Andric   ProcessSP GetProcess() {
5197480093f4SDimitry Andric     return m_debugger.GetCommandInterpreter()
5198480093f4SDimitry Andric         .GetExecutionContext()
5199480093f4SDimitry Andric         .GetProcessSP();
5200480093f4SDimitry Andric   }
5201480093f4SDimitry Andric 
TreeDelegateShouldDraw()5202349cc55cSDimitry Andric   bool TreeDelegateShouldDraw() override {
5203349cc55cSDimitry Andric     ProcessSP process = GetProcess();
5204349cc55cSDimitry Andric     if (!process)
5205349cc55cSDimitry Andric       return false;
5206349cc55cSDimitry Andric 
5207349cc55cSDimitry Andric     if (StateIsRunningState(process->GetState()))
5208349cc55cSDimitry Andric       return false;
5209349cc55cSDimitry Andric 
5210349cc55cSDimitry Andric     return true;
5211349cc55cSDimitry Andric   }
5212349cc55cSDimitry Andric 
TreeDelegateDrawTreeItem(TreeItem & item,Window & window)5213480093f4SDimitry Andric   void TreeDelegateDrawTreeItem(TreeItem &item, Window &window) override {
5214480093f4SDimitry Andric     ProcessSP process_sp = GetProcess();
5215480093f4SDimitry Andric     if (process_sp && process_sp->IsAlive()) {
5216480093f4SDimitry Andric       StreamString strm;
5217480093f4SDimitry Andric       ExecutionContext exe_ctx(process_sp);
5218480093f4SDimitry Andric       if (FormatEntity::Format(m_format, strm, nullptr, &exe_ctx, nullptr,
5219480093f4SDimitry Andric                                nullptr, false, false)) {
5220480093f4SDimitry Andric         int right_pad = 1;
5221e8d8bef9SDimitry Andric         window.PutCStringTruncated(right_pad, strm.GetString().str().c_str());
5222480093f4SDimitry Andric       }
5223480093f4SDimitry Andric     }
5224480093f4SDimitry Andric   }
5225480093f4SDimitry Andric 
TreeDelegateGenerateChildren(TreeItem & item)5226480093f4SDimitry Andric   void TreeDelegateGenerateChildren(TreeItem &item) override {
5227480093f4SDimitry Andric     ProcessSP process_sp = GetProcess();
5228fe6060f1SDimitry Andric     m_update_selection = false;
5229480093f4SDimitry Andric     if (process_sp && process_sp->IsAlive()) {
5230480093f4SDimitry Andric       StateType state = process_sp->GetState();
5231480093f4SDimitry Andric       if (StateIsStoppedState(state, true)) {
5232480093f4SDimitry Andric         const uint32_t stop_id = process_sp->GetStopID();
5233480093f4SDimitry Andric         if (m_stop_id == stop_id)
5234480093f4SDimitry Andric           return; // Children are already up to date
5235480093f4SDimitry Andric 
5236480093f4SDimitry Andric         m_stop_id = stop_id;
5237fe6060f1SDimitry Andric         m_update_selection = true;
5238480093f4SDimitry Andric 
5239480093f4SDimitry Andric         if (!m_thread_delegate_sp) {
5240480093f4SDimitry Andric           // Always expand the thread item the first time we show it
5241480093f4SDimitry Andric           // item.Expand();
5242480093f4SDimitry Andric           m_thread_delegate_sp =
5243480093f4SDimitry Andric               std::make_shared<ThreadTreeDelegate>(m_debugger);
5244480093f4SDimitry Andric         }
5245480093f4SDimitry Andric 
5246480093f4SDimitry Andric         ThreadList &threads = process_sp->GetThreadList();
5247480093f4SDimitry Andric         std::lock_guard<std::recursive_mutex> guard(threads.GetMutex());
5248fe6060f1SDimitry Andric         ThreadSP selected_thread = threads.GetSelectedThread();
5249480093f4SDimitry Andric         size_t num_threads = threads.GetSize();
5250*5f757f3fSDimitry Andric         item.Resize(num_threads, *m_thread_delegate_sp, false);
5251480093f4SDimitry Andric         for (size_t i = 0; i < num_threads; ++i) {
5252fe6060f1SDimitry Andric           ThreadSP thread = threads.GetThreadAtIndex(i);
5253fe6060f1SDimitry Andric           item[i].SetIdentifier(thread->GetID());
5254480093f4SDimitry Andric           item[i].SetMightHaveChildren(true);
5255fe6060f1SDimitry Andric           if (selected_thread->GetID() == thread->GetID())
5256fe6060f1SDimitry Andric             item[i].Expand();
5257480093f4SDimitry Andric         }
5258480093f4SDimitry Andric         return;
5259480093f4SDimitry Andric       }
5260480093f4SDimitry Andric     }
5261480093f4SDimitry Andric     item.ClearChildren();
5262480093f4SDimitry Andric   }
5263480093f4SDimitry Andric 
TreeDelegateUpdateSelection(TreeItem & root,int & selection_index,TreeItem * & selected_item)5264fe6060f1SDimitry Andric   void TreeDelegateUpdateSelection(TreeItem &root, int &selection_index,
5265fe6060f1SDimitry Andric                                    TreeItem *&selected_item) override {
5266fe6060f1SDimitry Andric     if (!m_update_selection)
5267fe6060f1SDimitry Andric       return;
5268fe6060f1SDimitry Andric 
5269fe6060f1SDimitry Andric     ProcessSP process_sp = GetProcess();
5270fe6060f1SDimitry Andric     if (!(process_sp && process_sp->IsAlive()))
5271fe6060f1SDimitry Andric       return;
5272fe6060f1SDimitry Andric 
5273fe6060f1SDimitry Andric     StateType state = process_sp->GetState();
5274fe6060f1SDimitry Andric     if (!StateIsStoppedState(state, true))
5275fe6060f1SDimitry Andric       return;
5276fe6060f1SDimitry Andric 
5277fe6060f1SDimitry Andric     ThreadList &threads = process_sp->GetThreadList();
5278fe6060f1SDimitry Andric     std::lock_guard<std::recursive_mutex> guard(threads.GetMutex());
5279fe6060f1SDimitry Andric     ThreadSP selected_thread = threads.GetSelectedThread();
5280fe6060f1SDimitry Andric     size_t num_threads = threads.GetSize();
5281fe6060f1SDimitry Andric     for (size_t i = 0; i < num_threads; ++i) {
5282fe6060f1SDimitry Andric       ThreadSP thread = threads.GetThreadAtIndex(i);
5283fe6060f1SDimitry Andric       if (selected_thread->GetID() == thread->GetID()) {
528406c3fb27SDimitry Andric         selected_item =
528506c3fb27SDimitry Andric             &root[i][thread->GetSelectedFrameIndex(SelectMostRelevantFrame)];
5286fe6060f1SDimitry Andric         selection_index = selected_item->GetRowIndex();
5287fe6060f1SDimitry Andric         return;
5288fe6060f1SDimitry Andric       }
5289fe6060f1SDimitry Andric     }
5290fe6060f1SDimitry Andric   }
5291fe6060f1SDimitry Andric 
TreeDelegateItemSelected(TreeItem & item)5292480093f4SDimitry Andric   bool TreeDelegateItemSelected(TreeItem &item) override { return false; }
5293480093f4SDimitry Andric 
TreeDelegateExpandRootByDefault()5294fe6060f1SDimitry Andric   bool TreeDelegateExpandRootByDefault() override { return true; }
5295fe6060f1SDimitry Andric 
5296480093f4SDimitry Andric protected:
5297480093f4SDimitry Andric   std::shared_ptr<ThreadTreeDelegate> m_thread_delegate_sp;
5298480093f4SDimitry Andric   Debugger &m_debugger;
529981ad6265SDimitry Andric   uint32_t m_stop_id = UINT32_MAX;
530081ad6265SDimitry Andric   bool m_update_selection = false;
5301480093f4SDimitry Andric   FormatEntity::Entry m_format;
5302480093f4SDimitry Andric };
5303480093f4SDimitry Andric 
5304349cc55cSDimitry Andric class BreakpointLocationTreeDelegate : public TreeDelegate {
5305349cc55cSDimitry Andric public:
BreakpointLocationTreeDelegate(Debugger & debugger)5306349cc55cSDimitry Andric   BreakpointLocationTreeDelegate(Debugger &debugger)
5307349cc55cSDimitry Andric       : TreeDelegate(), m_debugger(debugger) {}
5308349cc55cSDimitry Andric 
5309349cc55cSDimitry Andric   ~BreakpointLocationTreeDelegate() override = default;
5310349cc55cSDimitry Andric 
GetProcess()5311349cc55cSDimitry Andric   Process *GetProcess() {
5312349cc55cSDimitry Andric     ExecutionContext exe_ctx(
5313349cc55cSDimitry Andric         m_debugger.GetCommandInterpreter().GetExecutionContext());
5314349cc55cSDimitry Andric     return exe_ctx.GetProcessPtr();
5315349cc55cSDimitry Andric   }
5316349cc55cSDimitry Andric 
GetBreakpointLocation(const TreeItem & item)5317349cc55cSDimitry Andric   BreakpointLocationSP GetBreakpointLocation(const TreeItem &item) {
5318349cc55cSDimitry Andric     Breakpoint *breakpoint = (Breakpoint *)item.GetUserData();
5319349cc55cSDimitry Andric     return breakpoint->GetLocationAtIndex(item.GetIdentifier());
5320349cc55cSDimitry Andric   }
5321349cc55cSDimitry Andric 
TreeDelegateDrawTreeItem(TreeItem & item,Window & window)5322349cc55cSDimitry Andric   void TreeDelegateDrawTreeItem(TreeItem &item, Window &window) override {
5323349cc55cSDimitry Andric     BreakpointLocationSP breakpoint_location = GetBreakpointLocation(item);
5324349cc55cSDimitry Andric     Process *process = GetProcess();
5325349cc55cSDimitry Andric     StreamString stream;
5326349cc55cSDimitry Andric     stream.Printf("%i.%i: ", breakpoint_location->GetBreakpoint().GetID(),
5327349cc55cSDimitry Andric                   breakpoint_location->GetID());
5328349cc55cSDimitry Andric     Address address = breakpoint_location->GetAddress();
5329349cc55cSDimitry Andric     address.Dump(&stream, process, Address::DumpStyleResolvedDescription,
5330349cc55cSDimitry Andric                  Address::DumpStyleInvalid);
5331349cc55cSDimitry Andric     window.PutCStringTruncated(1, stream.GetString().str().c_str());
5332349cc55cSDimitry Andric   }
5333349cc55cSDimitry Andric 
ComputeDetailsList(BreakpointLocationSP breakpoint_location)5334349cc55cSDimitry Andric   StringList ComputeDetailsList(BreakpointLocationSP breakpoint_location) {
5335349cc55cSDimitry Andric     StringList details;
5336349cc55cSDimitry Andric 
5337349cc55cSDimitry Andric     Address address = breakpoint_location->GetAddress();
5338349cc55cSDimitry Andric     SymbolContext symbol_context;
5339349cc55cSDimitry Andric     address.CalculateSymbolContext(&symbol_context);
5340349cc55cSDimitry Andric 
5341349cc55cSDimitry Andric     if (symbol_context.module_sp) {
5342349cc55cSDimitry Andric       StreamString module_stream;
5343349cc55cSDimitry Andric       module_stream.PutCString("module = ");
5344349cc55cSDimitry Andric       symbol_context.module_sp->GetFileSpec().Dump(
5345349cc55cSDimitry Andric           module_stream.AsRawOstream());
5346349cc55cSDimitry Andric       details.AppendString(module_stream.GetString());
5347349cc55cSDimitry Andric     }
5348349cc55cSDimitry Andric 
5349349cc55cSDimitry Andric     if (symbol_context.comp_unit != nullptr) {
5350349cc55cSDimitry Andric       StreamString compile_unit_stream;
5351349cc55cSDimitry Andric       compile_unit_stream.PutCString("compile unit = ");
5352349cc55cSDimitry Andric       symbol_context.comp_unit->GetPrimaryFile().GetFilename().Dump(
5353349cc55cSDimitry Andric           &compile_unit_stream);
5354349cc55cSDimitry Andric       details.AppendString(compile_unit_stream.GetString());
5355349cc55cSDimitry Andric 
5356349cc55cSDimitry Andric       if (symbol_context.function != nullptr) {
5357349cc55cSDimitry Andric         StreamString function_stream;
5358349cc55cSDimitry Andric         function_stream.PutCString("function = ");
5359349cc55cSDimitry Andric         function_stream.PutCString(
5360349cc55cSDimitry Andric             symbol_context.function->GetName().AsCString("<unknown>"));
5361349cc55cSDimitry Andric         details.AppendString(function_stream.GetString());
5362349cc55cSDimitry Andric       }
5363349cc55cSDimitry Andric 
5364349cc55cSDimitry Andric       if (symbol_context.line_entry.line > 0) {
5365349cc55cSDimitry Andric         StreamString location_stream;
5366349cc55cSDimitry Andric         location_stream.PutCString("location = ");
5367349cc55cSDimitry Andric         symbol_context.line_entry.DumpStopContext(&location_stream, true);
5368349cc55cSDimitry Andric         details.AppendString(location_stream.GetString());
5369349cc55cSDimitry Andric       }
5370349cc55cSDimitry Andric 
5371349cc55cSDimitry Andric     } else {
5372349cc55cSDimitry Andric       if (symbol_context.symbol) {
5373349cc55cSDimitry Andric         StreamString symbol_stream;
5374349cc55cSDimitry Andric         if (breakpoint_location->IsReExported())
5375349cc55cSDimitry Andric           symbol_stream.PutCString("re-exported target = ");
5376349cc55cSDimitry Andric         else
5377349cc55cSDimitry Andric           symbol_stream.PutCString("symbol = ");
5378349cc55cSDimitry Andric         symbol_stream.PutCString(
5379349cc55cSDimitry Andric             symbol_context.symbol->GetName().AsCString("<unknown>"));
5380349cc55cSDimitry Andric         details.AppendString(symbol_stream.GetString());
5381349cc55cSDimitry Andric       }
5382349cc55cSDimitry Andric     }
5383349cc55cSDimitry Andric 
5384349cc55cSDimitry Andric     Process *process = GetProcess();
5385349cc55cSDimitry Andric 
5386349cc55cSDimitry Andric     StreamString address_stream;
5387349cc55cSDimitry Andric     address.Dump(&address_stream, process, Address::DumpStyleLoadAddress,
5388349cc55cSDimitry Andric                  Address::DumpStyleModuleWithFileAddress);
5389349cc55cSDimitry Andric     details.AppendString(address_stream.GetString());
5390349cc55cSDimitry Andric 
5391349cc55cSDimitry Andric     BreakpointSiteSP breakpoint_site = breakpoint_location->GetBreakpointSite();
5392349cc55cSDimitry Andric     if (breakpoint_location->IsIndirect() && breakpoint_site) {
5393349cc55cSDimitry Andric       Address resolved_address;
5394349cc55cSDimitry Andric       resolved_address.SetLoadAddress(breakpoint_site->GetLoadAddress(),
5395349cc55cSDimitry Andric                                       &breakpoint_location->GetTarget());
5396349cc55cSDimitry Andric       Symbol *resolved_symbol = resolved_address.CalculateSymbolContextSymbol();
5397349cc55cSDimitry Andric       if (resolved_symbol) {
5398349cc55cSDimitry Andric         StreamString indirect_target_stream;
5399349cc55cSDimitry Andric         indirect_target_stream.PutCString("indirect target = ");
5400349cc55cSDimitry Andric         indirect_target_stream.PutCString(
5401349cc55cSDimitry Andric             resolved_symbol->GetName().GetCString());
5402349cc55cSDimitry Andric         details.AppendString(indirect_target_stream.GetString());
5403349cc55cSDimitry Andric       }
5404349cc55cSDimitry Andric     }
5405349cc55cSDimitry Andric 
5406349cc55cSDimitry Andric     bool is_resolved = breakpoint_location->IsResolved();
5407349cc55cSDimitry Andric     StreamString resolved_stream;
5408349cc55cSDimitry Andric     resolved_stream.Printf("resolved = %s", is_resolved ? "true" : "false");
5409349cc55cSDimitry Andric     details.AppendString(resolved_stream.GetString());
5410349cc55cSDimitry Andric 
5411349cc55cSDimitry Andric     bool is_hardware = is_resolved && breakpoint_site->IsHardware();
5412349cc55cSDimitry Andric     StreamString hardware_stream;
5413349cc55cSDimitry Andric     hardware_stream.Printf("hardware = %s", is_hardware ? "true" : "false");
5414349cc55cSDimitry Andric     details.AppendString(hardware_stream.GetString());
5415349cc55cSDimitry Andric 
5416349cc55cSDimitry Andric     StreamString hit_count_stream;
5417349cc55cSDimitry Andric     hit_count_stream.Printf("hit count = %-4u",
5418349cc55cSDimitry Andric                             breakpoint_location->GetHitCount());
5419349cc55cSDimitry Andric     details.AppendString(hit_count_stream.GetString());
5420349cc55cSDimitry Andric 
5421349cc55cSDimitry Andric     return details;
5422349cc55cSDimitry Andric   }
5423349cc55cSDimitry Andric 
TreeDelegateGenerateChildren(TreeItem & item)5424349cc55cSDimitry Andric   void TreeDelegateGenerateChildren(TreeItem &item) override {
5425349cc55cSDimitry Andric     BreakpointLocationSP breakpoint_location = GetBreakpointLocation(item);
5426349cc55cSDimitry Andric     StringList details = ComputeDetailsList(breakpoint_location);
5427349cc55cSDimitry Andric 
5428349cc55cSDimitry Andric     if (!m_string_delegate_sp)
5429349cc55cSDimitry Andric       m_string_delegate_sp = std::make_shared<TextTreeDelegate>();
5430349cc55cSDimitry Andric 
5431*5f757f3fSDimitry Andric     item.Resize(details.GetSize(), *m_string_delegate_sp, false);
5432349cc55cSDimitry Andric     for (size_t i = 0; i < details.GetSize(); i++) {
5433349cc55cSDimitry Andric       item[i].SetText(details.GetStringAtIndex(i));
5434349cc55cSDimitry Andric     }
5435349cc55cSDimitry Andric   }
5436349cc55cSDimitry Andric 
TreeDelegateItemSelected(TreeItem & item)5437349cc55cSDimitry Andric   bool TreeDelegateItemSelected(TreeItem &item) override { return false; }
5438349cc55cSDimitry Andric 
5439349cc55cSDimitry Andric protected:
5440349cc55cSDimitry Andric   Debugger &m_debugger;
5441349cc55cSDimitry Andric   std::shared_ptr<TextTreeDelegate> m_string_delegate_sp;
5442349cc55cSDimitry Andric };
5443349cc55cSDimitry Andric 
5444349cc55cSDimitry Andric class BreakpointTreeDelegate : public TreeDelegate {
5445349cc55cSDimitry Andric public:
BreakpointTreeDelegate(Debugger & debugger)5446349cc55cSDimitry Andric   BreakpointTreeDelegate(Debugger &debugger)
5447349cc55cSDimitry Andric       : TreeDelegate(), m_debugger(debugger),
5448349cc55cSDimitry Andric         m_breakpoint_location_delegate_sp() {}
5449349cc55cSDimitry Andric 
5450349cc55cSDimitry Andric   ~BreakpointTreeDelegate() override = default;
5451349cc55cSDimitry Andric 
GetBreakpoint(const TreeItem & item)5452349cc55cSDimitry Andric   BreakpointSP GetBreakpoint(const TreeItem &item) {
5453349cc55cSDimitry Andric     TargetSP target = m_debugger.GetSelectedTarget();
5454349cc55cSDimitry Andric     BreakpointList &breakpoints = target->GetBreakpointList(false);
5455349cc55cSDimitry Andric     return breakpoints.GetBreakpointAtIndex(item.GetIdentifier());
5456349cc55cSDimitry Andric   }
5457349cc55cSDimitry Andric 
TreeDelegateDrawTreeItem(TreeItem & item,Window & window)5458349cc55cSDimitry Andric   void TreeDelegateDrawTreeItem(TreeItem &item, Window &window) override {
5459349cc55cSDimitry Andric     BreakpointSP breakpoint = GetBreakpoint(item);
5460349cc55cSDimitry Andric     StreamString stream;
5461349cc55cSDimitry Andric     stream.Format("{0}: ", breakpoint->GetID());
5462349cc55cSDimitry Andric     breakpoint->GetResolverDescription(&stream);
5463349cc55cSDimitry Andric     breakpoint->GetFilterDescription(&stream);
5464349cc55cSDimitry Andric     window.PutCStringTruncated(1, stream.GetString().str().c_str());
5465349cc55cSDimitry Andric   }
5466349cc55cSDimitry Andric 
TreeDelegateGenerateChildren(TreeItem & item)5467349cc55cSDimitry Andric   void TreeDelegateGenerateChildren(TreeItem &item) override {
5468349cc55cSDimitry Andric     BreakpointSP breakpoint = GetBreakpoint(item);
5469349cc55cSDimitry Andric 
5470349cc55cSDimitry Andric     if (!m_breakpoint_location_delegate_sp)
5471349cc55cSDimitry Andric       m_breakpoint_location_delegate_sp =
5472349cc55cSDimitry Andric           std::make_shared<BreakpointLocationTreeDelegate>(m_debugger);
5473349cc55cSDimitry Andric 
5474*5f757f3fSDimitry Andric     item.Resize(breakpoint->GetNumLocations(),
5475*5f757f3fSDimitry Andric                 *m_breakpoint_location_delegate_sp, true);
5476349cc55cSDimitry Andric     for (size_t i = 0; i < breakpoint->GetNumLocations(); i++) {
5477349cc55cSDimitry Andric       item[i].SetIdentifier(i);
5478349cc55cSDimitry Andric       item[i].SetUserData(breakpoint.get());
5479349cc55cSDimitry Andric     }
5480349cc55cSDimitry Andric   }
5481349cc55cSDimitry Andric 
TreeDelegateItemSelected(TreeItem & item)5482349cc55cSDimitry Andric   bool TreeDelegateItemSelected(TreeItem &item) override { return false; }
5483349cc55cSDimitry Andric 
5484349cc55cSDimitry Andric protected:
5485349cc55cSDimitry Andric   Debugger &m_debugger;
5486349cc55cSDimitry Andric   std::shared_ptr<BreakpointLocationTreeDelegate>
5487349cc55cSDimitry Andric       m_breakpoint_location_delegate_sp;
5488349cc55cSDimitry Andric };
5489349cc55cSDimitry Andric 
5490349cc55cSDimitry Andric class BreakpointsTreeDelegate : public TreeDelegate {
5491349cc55cSDimitry Andric public:
BreakpointsTreeDelegate(Debugger & debugger)5492349cc55cSDimitry Andric   BreakpointsTreeDelegate(Debugger &debugger)
5493349cc55cSDimitry Andric       : TreeDelegate(), m_debugger(debugger), m_breakpoint_delegate_sp() {}
5494349cc55cSDimitry Andric 
5495349cc55cSDimitry Andric   ~BreakpointsTreeDelegate() override = default;
5496349cc55cSDimitry Andric 
TreeDelegateShouldDraw()5497349cc55cSDimitry Andric   bool TreeDelegateShouldDraw() override {
5498349cc55cSDimitry Andric     TargetSP target = m_debugger.GetSelectedTarget();
5499349cc55cSDimitry Andric     if (!target)
5500349cc55cSDimitry Andric       return false;
5501349cc55cSDimitry Andric 
5502349cc55cSDimitry Andric     return true;
5503349cc55cSDimitry Andric   }
5504349cc55cSDimitry Andric 
TreeDelegateDrawTreeItem(TreeItem & item,Window & window)5505349cc55cSDimitry Andric   void TreeDelegateDrawTreeItem(TreeItem &item, Window &window) override {
5506349cc55cSDimitry Andric     window.PutCString("Breakpoints");
5507349cc55cSDimitry Andric   }
5508349cc55cSDimitry Andric 
TreeDelegateGenerateChildren(TreeItem & item)5509349cc55cSDimitry Andric   void TreeDelegateGenerateChildren(TreeItem &item) override {
5510349cc55cSDimitry Andric     TargetSP target = m_debugger.GetSelectedTarget();
5511349cc55cSDimitry Andric 
5512349cc55cSDimitry Andric     BreakpointList &breakpoints = target->GetBreakpointList(false);
5513349cc55cSDimitry Andric     std::unique_lock<std::recursive_mutex> lock;
5514349cc55cSDimitry Andric     breakpoints.GetListMutex(lock);
5515349cc55cSDimitry Andric 
5516349cc55cSDimitry Andric     if (!m_breakpoint_delegate_sp)
5517349cc55cSDimitry Andric       m_breakpoint_delegate_sp =
5518349cc55cSDimitry Andric           std::make_shared<BreakpointTreeDelegate>(m_debugger);
5519349cc55cSDimitry Andric 
5520*5f757f3fSDimitry Andric     item.Resize(breakpoints.GetSize(), *m_breakpoint_delegate_sp, true);
5521349cc55cSDimitry Andric     for (size_t i = 0; i < breakpoints.GetSize(); i++) {
5522349cc55cSDimitry Andric       item[i].SetIdentifier(i);
5523349cc55cSDimitry Andric     }
5524349cc55cSDimitry Andric   }
5525349cc55cSDimitry Andric 
TreeDelegateItemSelected(TreeItem & item)5526349cc55cSDimitry Andric   bool TreeDelegateItemSelected(TreeItem &item) override { return false; }
5527349cc55cSDimitry Andric 
TreeDelegateExpandRootByDefault()5528349cc55cSDimitry Andric   bool TreeDelegateExpandRootByDefault() override { return true; }
5529349cc55cSDimitry Andric 
5530349cc55cSDimitry Andric protected:
5531349cc55cSDimitry Andric   Debugger &m_debugger;
5532349cc55cSDimitry Andric   std::shared_ptr<BreakpointTreeDelegate> m_breakpoint_delegate_sp;
5533349cc55cSDimitry Andric };
5534349cc55cSDimitry Andric 
5535480093f4SDimitry Andric class ValueObjectListDelegate : public WindowDelegate {
5536480093f4SDimitry Andric public:
ValueObjectListDelegate()5537fe6060f1SDimitry Andric   ValueObjectListDelegate() : m_rows() {}
5538480093f4SDimitry Andric 
ValueObjectListDelegate(ValueObjectList & valobj_list)553981ad6265SDimitry Andric   ValueObjectListDelegate(ValueObjectList &valobj_list) : m_rows() {
5540480093f4SDimitry Andric     SetValues(valobj_list);
5541480093f4SDimitry Andric   }
5542480093f4SDimitry Andric 
5543480093f4SDimitry Andric   ~ValueObjectListDelegate() override = default;
5544480093f4SDimitry Andric 
SetValues(ValueObjectList & valobj_list)5545480093f4SDimitry Andric   void SetValues(ValueObjectList &valobj_list) {
5546480093f4SDimitry Andric     m_selected_row = nullptr;
5547480093f4SDimitry Andric     m_selected_row_idx = 0;
5548480093f4SDimitry Andric     m_first_visible_row = 0;
5549480093f4SDimitry Andric     m_num_rows = 0;
5550480093f4SDimitry Andric     m_rows.clear();
5551480093f4SDimitry Andric     for (auto &valobj_sp : valobj_list.GetObjects())
5552480093f4SDimitry Andric       m_rows.push_back(Row(valobj_sp, nullptr));
5553480093f4SDimitry Andric   }
5554480093f4SDimitry Andric 
WindowDelegateDraw(Window & window,bool force)5555480093f4SDimitry Andric   bool WindowDelegateDraw(Window &window, bool force) override {
5556480093f4SDimitry Andric     m_num_rows = 0;
5557480093f4SDimitry Andric     m_min_x = 2;
5558480093f4SDimitry Andric     m_min_y = 1;
5559480093f4SDimitry Andric     m_max_x = window.GetWidth() - 1;
5560480093f4SDimitry Andric     m_max_y = window.GetHeight() - 1;
5561480093f4SDimitry Andric 
5562480093f4SDimitry Andric     window.Erase();
5563480093f4SDimitry Andric     window.DrawTitleBox(window.GetName());
5564480093f4SDimitry Andric 
5565480093f4SDimitry Andric     const int num_visible_rows = NumVisibleRows();
5566480093f4SDimitry Andric     const int num_rows = CalculateTotalNumberRows(m_rows);
5567480093f4SDimitry Andric 
5568480093f4SDimitry Andric     // If we unexpanded while having something selected our total number of
5569480093f4SDimitry Andric     // rows is less than the num visible rows, then make sure we show all the
5570480093f4SDimitry Andric     // rows by setting the first visible row accordingly.
5571480093f4SDimitry Andric     if (m_first_visible_row > 0 && num_rows < num_visible_rows)
5572480093f4SDimitry Andric       m_first_visible_row = 0;
5573480093f4SDimitry Andric 
5574480093f4SDimitry Andric     // Make sure the selected row is always visible
5575480093f4SDimitry Andric     if (m_selected_row_idx < m_first_visible_row)
5576480093f4SDimitry Andric       m_first_visible_row = m_selected_row_idx;
5577480093f4SDimitry Andric     else if (m_first_visible_row + num_visible_rows <= m_selected_row_idx)
5578480093f4SDimitry Andric       m_first_visible_row = m_selected_row_idx - num_visible_rows + 1;
5579480093f4SDimitry Andric 
5580480093f4SDimitry Andric     DisplayRows(window, m_rows, g_options);
5581480093f4SDimitry Andric 
5582480093f4SDimitry Andric     // Get the selected row
5583480093f4SDimitry Andric     m_selected_row = GetRowForRowIndex(m_selected_row_idx);
5584480093f4SDimitry Andric     // Keep the cursor on the selected row so the highlight and the cursor are
5585480093f4SDimitry Andric     // always on the same line
5586480093f4SDimitry Andric     if (m_selected_row)
5587480093f4SDimitry Andric       window.MoveCursor(m_selected_row->x, m_selected_row->y);
5588480093f4SDimitry Andric 
5589480093f4SDimitry Andric     return true; // Drawing handled
5590480093f4SDimitry Andric   }
5591480093f4SDimitry Andric 
WindowDelegateGetKeyHelp()5592480093f4SDimitry Andric   KeyHelp *WindowDelegateGetKeyHelp() override {
5593480093f4SDimitry Andric     static curses::KeyHelp g_source_view_key_help[] = {
5594480093f4SDimitry Andric         {KEY_UP, "Select previous item"},
5595480093f4SDimitry Andric         {KEY_DOWN, "Select next item"},
5596480093f4SDimitry Andric         {KEY_RIGHT, "Expand selected item"},
5597480093f4SDimitry Andric         {KEY_LEFT, "Unexpand selected item or select parent if not expanded"},
5598480093f4SDimitry Andric         {KEY_PPAGE, "Page up"},
5599480093f4SDimitry Andric         {KEY_NPAGE, "Page down"},
5600480093f4SDimitry Andric         {'A', "Format as annotated address"},
5601480093f4SDimitry Andric         {'b', "Format as binary"},
5602480093f4SDimitry Andric         {'B', "Format as hex bytes with ASCII"},
5603480093f4SDimitry Andric         {'c', "Format as character"},
5604480093f4SDimitry Andric         {'d', "Format as a signed integer"},
5605480093f4SDimitry Andric         {'D', "Format selected value using the default format for the type"},
5606480093f4SDimitry Andric         {'f', "Format as float"},
5607480093f4SDimitry Andric         {'h', "Show help dialog"},
5608480093f4SDimitry Andric         {'i', "Format as instructions"},
5609480093f4SDimitry Andric         {'o', "Format as octal"},
5610480093f4SDimitry Andric         {'p', "Format as pointer"},
5611480093f4SDimitry Andric         {'s', "Format as C string"},
5612480093f4SDimitry Andric         {'t', "Toggle showing/hiding type names"},
5613480093f4SDimitry Andric         {'u', "Format as an unsigned integer"},
5614480093f4SDimitry Andric         {'x', "Format as hex"},
5615480093f4SDimitry Andric         {'X', "Format as uppercase hex"},
5616480093f4SDimitry Andric         {' ', "Toggle item expansion"},
5617480093f4SDimitry Andric         {',', "Page up"},
5618480093f4SDimitry Andric         {'.', "Page down"},
5619480093f4SDimitry Andric         {'\0', nullptr}};
5620480093f4SDimitry Andric     return g_source_view_key_help;
5621480093f4SDimitry Andric   }
5622480093f4SDimitry Andric 
WindowDelegateHandleChar(Window & window,int c)5623480093f4SDimitry Andric   HandleCharResult WindowDelegateHandleChar(Window &window, int c) override {
5624480093f4SDimitry Andric     switch (c) {
5625480093f4SDimitry Andric     case 'x':
5626480093f4SDimitry Andric     case 'X':
5627480093f4SDimitry Andric     case 'o':
5628480093f4SDimitry Andric     case 's':
5629480093f4SDimitry Andric     case 'u':
5630480093f4SDimitry Andric     case 'd':
5631480093f4SDimitry Andric     case 'D':
5632480093f4SDimitry Andric     case 'i':
5633480093f4SDimitry Andric     case 'A':
5634480093f4SDimitry Andric     case 'p':
5635480093f4SDimitry Andric     case 'c':
5636480093f4SDimitry Andric     case 'b':
5637480093f4SDimitry Andric     case 'B':
5638480093f4SDimitry Andric     case 'f':
5639480093f4SDimitry Andric       // Change the format for the currently selected item
5640480093f4SDimitry Andric       if (m_selected_row) {
5641480093f4SDimitry Andric         auto valobj_sp = m_selected_row->value.GetSP();
5642480093f4SDimitry Andric         if (valobj_sp)
5643480093f4SDimitry Andric           valobj_sp->SetFormat(FormatForChar(c));
5644480093f4SDimitry Andric       }
5645480093f4SDimitry Andric       return eKeyHandled;
5646480093f4SDimitry Andric 
5647480093f4SDimitry Andric     case 't':
5648480093f4SDimitry Andric       // Toggle showing type names
5649480093f4SDimitry Andric       g_options.show_types = !g_options.show_types;
5650480093f4SDimitry Andric       return eKeyHandled;
5651480093f4SDimitry Andric 
5652480093f4SDimitry Andric     case ',':
5653480093f4SDimitry Andric     case KEY_PPAGE:
5654480093f4SDimitry Andric       // Page up key
5655480093f4SDimitry Andric       if (m_first_visible_row > 0) {
5656480093f4SDimitry Andric         if (static_cast<int>(m_first_visible_row) > m_max_y)
5657480093f4SDimitry Andric           m_first_visible_row -= m_max_y;
5658480093f4SDimitry Andric         else
5659480093f4SDimitry Andric           m_first_visible_row = 0;
5660480093f4SDimitry Andric         m_selected_row_idx = m_first_visible_row;
5661480093f4SDimitry Andric       }
5662480093f4SDimitry Andric       return eKeyHandled;
5663480093f4SDimitry Andric 
5664480093f4SDimitry Andric     case '.':
5665480093f4SDimitry Andric     case KEY_NPAGE:
5666480093f4SDimitry Andric       // Page down key
5667480093f4SDimitry Andric       if (m_num_rows > static_cast<size_t>(m_max_y)) {
5668480093f4SDimitry Andric         if (m_first_visible_row + m_max_y < m_num_rows) {
5669480093f4SDimitry Andric           m_first_visible_row += m_max_y;
5670480093f4SDimitry Andric           m_selected_row_idx = m_first_visible_row;
5671480093f4SDimitry Andric         }
5672480093f4SDimitry Andric       }
5673480093f4SDimitry Andric       return eKeyHandled;
5674480093f4SDimitry Andric 
5675480093f4SDimitry Andric     case KEY_UP:
5676480093f4SDimitry Andric       if (m_selected_row_idx > 0)
5677480093f4SDimitry Andric         --m_selected_row_idx;
5678480093f4SDimitry Andric       return eKeyHandled;
5679480093f4SDimitry Andric 
5680480093f4SDimitry Andric     case KEY_DOWN:
5681480093f4SDimitry Andric       if (m_selected_row_idx + 1 < m_num_rows)
5682480093f4SDimitry Andric         ++m_selected_row_idx;
5683480093f4SDimitry Andric       return eKeyHandled;
5684480093f4SDimitry Andric 
5685480093f4SDimitry Andric     case KEY_RIGHT:
5686480093f4SDimitry Andric       if (m_selected_row) {
5687480093f4SDimitry Andric         if (!m_selected_row->expanded)
5688480093f4SDimitry Andric           m_selected_row->Expand();
5689480093f4SDimitry Andric       }
5690480093f4SDimitry Andric       return eKeyHandled;
5691480093f4SDimitry Andric 
5692480093f4SDimitry Andric     case KEY_LEFT:
5693480093f4SDimitry Andric       if (m_selected_row) {
5694480093f4SDimitry Andric         if (m_selected_row->expanded)
5695480093f4SDimitry Andric           m_selected_row->Unexpand();
5696480093f4SDimitry Andric         else if (m_selected_row->parent)
5697480093f4SDimitry Andric           m_selected_row_idx = m_selected_row->parent->row_idx;
5698480093f4SDimitry Andric       }
5699480093f4SDimitry Andric       return eKeyHandled;
5700480093f4SDimitry Andric 
5701480093f4SDimitry Andric     case ' ':
5702480093f4SDimitry Andric       // Toggle expansion state when SPACE is pressed
5703480093f4SDimitry Andric       if (m_selected_row) {
5704480093f4SDimitry Andric         if (m_selected_row->expanded)
5705480093f4SDimitry Andric           m_selected_row->Unexpand();
5706480093f4SDimitry Andric         else
5707480093f4SDimitry Andric           m_selected_row->Expand();
5708480093f4SDimitry Andric       }
5709480093f4SDimitry Andric       return eKeyHandled;
5710480093f4SDimitry Andric 
5711480093f4SDimitry Andric     case 'h':
5712480093f4SDimitry Andric       window.CreateHelpSubwindow();
5713480093f4SDimitry Andric       return eKeyHandled;
5714480093f4SDimitry Andric 
5715480093f4SDimitry Andric     default:
5716480093f4SDimitry Andric       break;
5717480093f4SDimitry Andric     }
5718480093f4SDimitry Andric     return eKeyNotHandled;
5719480093f4SDimitry Andric   }
5720480093f4SDimitry Andric 
5721480093f4SDimitry Andric protected:
5722480093f4SDimitry Andric   std::vector<Row> m_rows;
5723fe6060f1SDimitry Andric   Row *m_selected_row = nullptr;
5724fe6060f1SDimitry Andric   uint32_t m_selected_row_idx = 0;
5725fe6060f1SDimitry Andric   uint32_t m_first_visible_row = 0;
5726fe6060f1SDimitry Andric   uint32_t m_num_rows = 0;
5727bdd1243dSDimitry Andric   int m_min_x = 0;
5728bdd1243dSDimitry Andric   int m_min_y = 0;
5729fe6060f1SDimitry Andric   int m_max_x = 0;
5730fe6060f1SDimitry Andric   int m_max_y = 0;
5731480093f4SDimitry Andric 
FormatForChar(int c)5732480093f4SDimitry Andric   static Format FormatForChar(int c) {
5733480093f4SDimitry Andric     switch (c) {
5734480093f4SDimitry Andric     case 'x':
5735480093f4SDimitry Andric       return eFormatHex;
5736480093f4SDimitry Andric     case 'X':
5737480093f4SDimitry Andric       return eFormatHexUppercase;
5738480093f4SDimitry Andric     case 'o':
5739480093f4SDimitry Andric       return eFormatOctal;
5740480093f4SDimitry Andric     case 's':
5741480093f4SDimitry Andric       return eFormatCString;
5742480093f4SDimitry Andric     case 'u':
5743480093f4SDimitry Andric       return eFormatUnsigned;
5744480093f4SDimitry Andric     case 'd':
5745480093f4SDimitry Andric       return eFormatDecimal;
5746480093f4SDimitry Andric     case 'D':
5747480093f4SDimitry Andric       return eFormatDefault;
5748480093f4SDimitry Andric     case 'i':
5749480093f4SDimitry Andric       return eFormatInstruction;
5750480093f4SDimitry Andric     case 'A':
5751480093f4SDimitry Andric       return eFormatAddressInfo;
5752480093f4SDimitry Andric     case 'p':
5753480093f4SDimitry Andric       return eFormatPointer;
5754480093f4SDimitry Andric     case 'c':
5755480093f4SDimitry Andric       return eFormatChar;
5756480093f4SDimitry Andric     case 'b':
5757480093f4SDimitry Andric       return eFormatBinary;
5758480093f4SDimitry Andric     case 'B':
5759480093f4SDimitry Andric       return eFormatBytesWithASCII;
5760480093f4SDimitry Andric     case 'f':
5761480093f4SDimitry Andric       return eFormatFloat;
5762480093f4SDimitry Andric     }
5763480093f4SDimitry Andric     return eFormatDefault;
5764480093f4SDimitry Andric   }
5765480093f4SDimitry Andric 
DisplayRowObject(Window & window,Row & row,DisplayOptions & options,bool highlight,bool last_child)5766480093f4SDimitry Andric   bool DisplayRowObject(Window &window, Row &row, DisplayOptions &options,
5767480093f4SDimitry Andric                         bool highlight, bool last_child) {
5768480093f4SDimitry Andric     ValueObject *valobj = row.value.GetSP().get();
5769480093f4SDimitry Andric 
5770480093f4SDimitry Andric     if (valobj == nullptr)
5771480093f4SDimitry Andric       return false;
5772480093f4SDimitry Andric 
5773480093f4SDimitry Andric     const char *type_name =
5774480093f4SDimitry Andric         options.show_types ? valobj->GetTypeName().GetCString() : nullptr;
5775480093f4SDimitry Andric     const char *name = valobj->GetName().GetCString();
5776480093f4SDimitry Andric     const char *value = valobj->GetValueAsCString();
5777480093f4SDimitry Andric     const char *summary = valobj->GetSummaryAsCString();
5778480093f4SDimitry Andric 
5779480093f4SDimitry Andric     window.MoveCursor(row.x, row.y);
5780480093f4SDimitry Andric 
5781480093f4SDimitry Andric     row.DrawTree(window);
5782480093f4SDimitry Andric 
5783480093f4SDimitry Andric     if (highlight)
5784480093f4SDimitry Andric       window.AttributeOn(A_REVERSE);
5785480093f4SDimitry Andric 
5786480093f4SDimitry Andric     if (type_name && type_name[0])
5787e8d8bef9SDimitry Andric       window.PrintfTruncated(1, "(%s) ", type_name);
5788480093f4SDimitry Andric 
5789480093f4SDimitry Andric     if (name && name[0])
5790e8d8bef9SDimitry Andric       window.PutCStringTruncated(1, name);
5791480093f4SDimitry Andric 
5792480093f4SDimitry Andric     attr_t changd_attr = 0;
5793480093f4SDimitry Andric     if (valobj->GetValueDidChange())
5794e8d8bef9SDimitry Andric       changd_attr = COLOR_PAIR(RedOnBlack) | A_BOLD;
5795480093f4SDimitry Andric 
5796480093f4SDimitry Andric     if (value && value[0]) {
5797e8d8bef9SDimitry Andric       window.PutCStringTruncated(1, " = ");
5798480093f4SDimitry Andric       if (changd_attr)
5799480093f4SDimitry Andric         window.AttributeOn(changd_attr);
5800e8d8bef9SDimitry Andric       window.PutCStringTruncated(1, value);
5801480093f4SDimitry Andric       if (changd_attr)
5802480093f4SDimitry Andric         window.AttributeOff(changd_attr);
5803480093f4SDimitry Andric     }
5804480093f4SDimitry Andric 
5805480093f4SDimitry Andric     if (summary && summary[0]) {
5806e8d8bef9SDimitry Andric       window.PutCStringTruncated(1, " ");
5807480093f4SDimitry Andric       if (changd_attr)
5808480093f4SDimitry Andric         window.AttributeOn(changd_attr);
5809e8d8bef9SDimitry Andric       window.PutCStringTruncated(1, summary);
5810480093f4SDimitry Andric       if (changd_attr)
5811480093f4SDimitry Andric         window.AttributeOff(changd_attr);
5812480093f4SDimitry Andric     }
5813480093f4SDimitry Andric 
5814480093f4SDimitry Andric     if (highlight)
5815480093f4SDimitry Andric       window.AttributeOff(A_REVERSE);
5816480093f4SDimitry Andric 
5817480093f4SDimitry Andric     return true;
5818480093f4SDimitry Andric   }
5819480093f4SDimitry Andric 
DisplayRows(Window & window,std::vector<Row> & rows,DisplayOptions & options)5820480093f4SDimitry Andric   void DisplayRows(Window &window, std::vector<Row> &rows,
5821480093f4SDimitry Andric                    DisplayOptions &options) {
5822480093f4SDimitry Andric     // >   0x25B7
5823480093f4SDimitry Andric     // \/  0x25BD
5824480093f4SDimitry Andric 
5825480093f4SDimitry Andric     bool window_is_active = window.IsActive();
5826480093f4SDimitry Andric     for (auto &row : rows) {
5827480093f4SDimitry Andric       const bool last_child = row.parent && &rows[rows.size() - 1] == &row;
5828480093f4SDimitry Andric       // Save the row index in each Row structure
5829480093f4SDimitry Andric       row.row_idx = m_num_rows;
5830480093f4SDimitry Andric       if ((m_num_rows >= m_first_visible_row) &&
5831480093f4SDimitry Andric           ((m_num_rows - m_first_visible_row) <
5832480093f4SDimitry Andric            static_cast<size_t>(NumVisibleRows()))) {
5833480093f4SDimitry Andric         row.x = m_min_x;
5834480093f4SDimitry Andric         row.y = m_num_rows - m_first_visible_row + 1;
5835480093f4SDimitry Andric         if (DisplayRowObject(window, row, options,
5836480093f4SDimitry Andric                              window_is_active &&
5837480093f4SDimitry Andric                                  m_num_rows == m_selected_row_idx,
5838480093f4SDimitry Andric                              last_child)) {
5839480093f4SDimitry Andric           ++m_num_rows;
5840480093f4SDimitry Andric         } else {
5841480093f4SDimitry Andric           row.x = 0;
5842480093f4SDimitry Andric           row.y = 0;
5843480093f4SDimitry Andric         }
5844480093f4SDimitry Andric       } else {
5845480093f4SDimitry Andric         row.x = 0;
5846480093f4SDimitry Andric         row.y = 0;
5847480093f4SDimitry Andric         ++m_num_rows;
5848480093f4SDimitry Andric       }
5849480093f4SDimitry Andric 
585081ad6265SDimitry Andric       if (row.expanded) {
5851480093f4SDimitry Andric         auto &children = row.GetChildren();
585281ad6265SDimitry Andric         if (!children.empty()) {
5853480093f4SDimitry Andric           DisplayRows(window, children, options);
5854480093f4SDimitry Andric         }
5855480093f4SDimitry Andric       }
5856480093f4SDimitry Andric     }
585781ad6265SDimitry Andric   }
5858480093f4SDimitry Andric 
CalculateTotalNumberRows(std::vector<Row> & rows)5859480093f4SDimitry Andric   int CalculateTotalNumberRows(std::vector<Row> &rows) {
5860480093f4SDimitry Andric     int row_count = 0;
5861480093f4SDimitry Andric     for (auto &row : rows) {
5862480093f4SDimitry Andric       ++row_count;
5863480093f4SDimitry Andric       if (row.expanded)
5864480093f4SDimitry Andric         row_count += CalculateTotalNumberRows(row.GetChildren());
5865480093f4SDimitry Andric     }
5866480093f4SDimitry Andric     return row_count;
5867480093f4SDimitry Andric   }
5868480093f4SDimitry Andric 
GetRowForRowIndexImpl(std::vector<Row> & rows,size_t & row_index)5869480093f4SDimitry Andric   static Row *GetRowForRowIndexImpl(std::vector<Row> &rows, size_t &row_index) {
5870480093f4SDimitry Andric     for (auto &row : rows) {
5871480093f4SDimitry Andric       if (row_index == 0)
5872480093f4SDimitry Andric         return &row;
5873480093f4SDimitry Andric       else {
5874480093f4SDimitry Andric         --row_index;
587581ad6265SDimitry Andric         if (row.expanded) {
5876480093f4SDimitry Andric           auto &children = row.GetChildren();
587781ad6265SDimitry Andric           if (!children.empty()) {
5878480093f4SDimitry Andric             Row *result = GetRowForRowIndexImpl(children, row_index);
5879480093f4SDimitry Andric             if (result)
5880480093f4SDimitry Andric               return result;
5881480093f4SDimitry Andric           }
5882480093f4SDimitry Andric         }
5883480093f4SDimitry Andric       }
588481ad6265SDimitry Andric     }
5885480093f4SDimitry Andric     return nullptr;
5886480093f4SDimitry Andric   }
5887480093f4SDimitry Andric 
GetRowForRowIndex(size_t row_index)5888480093f4SDimitry Andric   Row *GetRowForRowIndex(size_t row_index) {
5889480093f4SDimitry Andric     return GetRowForRowIndexImpl(m_rows, row_index);
5890480093f4SDimitry Andric   }
5891480093f4SDimitry Andric 
NumVisibleRows() const5892480093f4SDimitry Andric   int NumVisibleRows() const { return m_max_y - m_min_y; }
5893480093f4SDimitry Andric 
5894480093f4SDimitry Andric   static DisplayOptions g_options;
5895480093f4SDimitry Andric };
5896480093f4SDimitry Andric 
5897480093f4SDimitry Andric class FrameVariablesWindowDelegate : public ValueObjectListDelegate {
5898480093f4SDimitry Andric public:
FrameVariablesWindowDelegate(Debugger & debugger)5899480093f4SDimitry Andric   FrameVariablesWindowDelegate(Debugger &debugger)
590081ad6265SDimitry Andric       : ValueObjectListDelegate(), m_debugger(debugger) {}
5901480093f4SDimitry Andric 
5902480093f4SDimitry Andric   ~FrameVariablesWindowDelegate() override = default;
5903480093f4SDimitry Andric 
WindowDelegateGetHelpText()5904480093f4SDimitry Andric   const char *WindowDelegateGetHelpText() override {
5905480093f4SDimitry Andric     return "Frame variable window keyboard shortcuts:";
5906480093f4SDimitry Andric   }
5907480093f4SDimitry Andric 
WindowDelegateDraw(Window & window,bool force)5908480093f4SDimitry Andric   bool WindowDelegateDraw(Window &window, bool force) override {
5909480093f4SDimitry Andric     ExecutionContext exe_ctx(
5910480093f4SDimitry Andric         m_debugger.GetCommandInterpreter().GetExecutionContext());
5911480093f4SDimitry Andric     Process *process = exe_ctx.GetProcessPtr();
5912480093f4SDimitry Andric     Block *frame_block = nullptr;
5913480093f4SDimitry Andric     StackFrame *frame = nullptr;
5914480093f4SDimitry Andric 
5915480093f4SDimitry Andric     if (process) {
5916480093f4SDimitry Andric       StateType state = process->GetState();
5917480093f4SDimitry Andric       if (StateIsStoppedState(state, true)) {
5918480093f4SDimitry Andric         frame = exe_ctx.GetFramePtr();
5919480093f4SDimitry Andric         if (frame)
5920480093f4SDimitry Andric           frame_block = frame->GetFrameBlock();
5921480093f4SDimitry Andric       } else if (StateIsRunningState(state)) {
5922480093f4SDimitry Andric         return true; // Don't do any updating when we are running
5923480093f4SDimitry Andric       }
5924480093f4SDimitry Andric     }
5925480093f4SDimitry Andric 
5926480093f4SDimitry Andric     ValueObjectList local_values;
5927480093f4SDimitry Andric     if (frame_block) {
5928480093f4SDimitry Andric       // Only update the variables if they have changed
5929480093f4SDimitry Andric       if (m_frame_block != frame_block) {
5930480093f4SDimitry Andric         m_frame_block = frame_block;
5931480093f4SDimitry Andric 
5932bdd1243dSDimitry Andric         VariableList *locals = frame->GetVariableList(true, nullptr);
5933480093f4SDimitry Andric         if (locals) {
5934480093f4SDimitry Andric           const DynamicValueType use_dynamic = eDynamicDontRunTarget;
5935480093f4SDimitry Andric           for (const VariableSP &local_sp : *locals) {
5936480093f4SDimitry Andric             ValueObjectSP value_sp =
5937480093f4SDimitry Andric                 frame->GetValueObjectForFrameVariable(local_sp, use_dynamic);
5938480093f4SDimitry Andric             if (value_sp) {
5939480093f4SDimitry Andric               ValueObjectSP synthetic_value_sp = value_sp->GetSyntheticValue();
5940480093f4SDimitry Andric               if (synthetic_value_sp)
5941480093f4SDimitry Andric                 local_values.Append(synthetic_value_sp);
5942480093f4SDimitry Andric               else
5943480093f4SDimitry Andric                 local_values.Append(value_sp);
5944480093f4SDimitry Andric             }
5945480093f4SDimitry Andric           }
5946480093f4SDimitry Andric           // Update the values
5947480093f4SDimitry Andric           SetValues(local_values);
5948480093f4SDimitry Andric         }
5949480093f4SDimitry Andric       }
5950480093f4SDimitry Andric     } else {
5951480093f4SDimitry Andric       m_frame_block = nullptr;
5952480093f4SDimitry Andric       // Update the values with an empty list if there is no frame
5953480093f4SDimitry Andric       SetValues(local_values);
5954480093f4SDimitry Andric     }
5955480093f4SDimitry Andric 
5956480093f4SDimitry Andric     return ValueObjectListDelegate::WindowDelegateDraw(window, force);
5957480093f4SDimitry Andric   }
5958480093f4SDimitry Andric 
5959480093f4SDimitry Andric protected:
5960480093f4SDimitry Andric   Debugger &m_debugger;
596181ad6265SDimitry Andric   Block *m_frame_block = nullptr;
5962480093f4SDimitry Andric };
5963480093f4SDimitry Andric 
5964480093f4SDimitry Andric class RegistersWindowDelegate : public ValueObjectListDelegate {
5965480093f4SDimitry Andric public:
RegistersWindowDelegate(Debugger & debugger)5966480093f4SDimitry Andric   RegistersWindowDelegate(Debugger &debugger)
5967480093f4SDimitry Andric       : ValueObjectListDelegate(), m_debugger(debugger) {}
5968480093f4SDimitry Andric 
5969480093f4SDimitry Andric   ~RegistersWindowDelegate() override = default;
5970480093f4SDimitry Andric 
WindowDelegateGetHelpText()5971480093f4SDimitry Andric   const char *WindowDelegateGetHelpText() override {
5972480093f4SDimitry Andric     return "Register window keyboard shortcuts:";
5973480093f4SDimitry Andric   }
5974480093f4SDimitry Andric 
WindowDelegateDraw(Window & window,bool force)5975480093f4SDimitry Andric   bool WindowDelegateDraw(Window &window, bool force) override {
5976480093f4SDimitry Andric     ExecutionContext exe_ctx(
5977480093f4SDimitry Andric         m_debugger.GetCommandInterpreter().GetExecutionContext());
5978480093f4SDimitry Andric     StackFrame *frame = exe_ctx.GetFramePtr();
5979480093f4SDimitry Andric 
5980480093f4SDimitry Andric     ValueObjectList value_list;
5981480093f4SDimitry Andric     if (frame) {
5982480093f4SDimitry Andric       if (frame->GetStackID() != m_stack_id) {
5983480093f4SDimitry Andric         m_stack_id = frame->GetStackID();
5984480093f4SDimitry Andric         RegisterContextSP reg_ctx(frame->GetRegisterContext());
5985480093f4SDimitry Andric         if (reg_ctx) {
5986480093f4SDimitry Andric           const uint32_t num_sets = reg_ctx->GetRegisterSetCount();
5987480093f4SDimitry Andric           for (uint32_t set_idx = 0; set_idx < num_sets; ++set_idx) {
5988480093f4SDimitry Andric             value_list.Append(
5989480093f4SDimitry Andric                 ValueObjectRegisterSet::Create(frame, reg_ctx, set_idx));
5990480093f4SDimitry Andric           }
5991480093f4SDimitry Andric         }
5992480093f4SDimitry Andric         SetValues(value_list);
5993480093f4SDimitry Andric       }
5994480093f4SDimitry Andric     } else {
5995480093f4SDimitry Andric       Process *process = exe_ctx.GetProcessPtr();
5996480093f4SDimitry Andric       if (process && process->IsAlive())
5997480093f4SDimitry Andric         return true; // Don't do any updating if we are running
5998480093f4SDimitry Andric       else {
5999480093f4SDimitry Andric         // Update the values with an empty list if there is no process or the
6000480093f4SDimitry Andric         // process isn't alive anymore
6001480093f4SDimitry Andric         SetValues(value_list);
6002480093f4SDimitry Andric       }
6003480093f4SDimitry Andric     }
6004480093f4SDimitry Andric     return ValueObjectListDelegate::WindowDelegateDraw(window, force);
6005480093f4SDimitry Andric   }
6006480093f4SDimitry Andric 
6007480093f4SDimitry Andric protected:
6008480093f4SDimitry Andric   Debugger &m_debugger;
6009480093f4SDimitry Andric   StackID m_stack_id;
6010480093f4SDimitry Andric };
6011480093f4SDimitry Andric 
CursesKeyToCString(int ch)6012480093f4SDimitry Andric static const char *CursesKeyToCString(int ch) {
6013480093f4SDimitry Andric   static char g_desc[32];
6014480093f4SDimitry Andric   if (ch >= KEY_F0 && ch < KEY_F0 + 64) {
6015480093f4SDimitry Andric     snprintf(g_desc, sizeof(g_desc), "F%u", ch - KEY_F0);
6016480093f4SDimitry Andric     return g_desc;
6017480093f4SDimitry Andric   }
6018480093f4SDimitry Andric   switch (ch) {
6019480093f4SDimitry Andric   case KEY_DOWN:
6020480093f4SDimitry Andric     return "down";
6021480093f4SDimitry Andric   case KEY_UP:
6022480093f4SDimitry Andric     return "up";
6023480093f4SDimitry Andric   case KEY_LEFT:
6024480093f4SDimitry Andric     return "left";
6025480093f4SDimitry Andric   case KEY_RIGHT:
6026480093f4SDimitry Andric     return "right";
6027480093f4SDimitry Andric   case KEY_HOME:
6028480093f4SDimitry Andric     return "home";
6029480093f4SDimitry Andric   case KEY_BACKSPACE:
6030480093f4SDimitry Andric     return "backspace";
6031480093f4SDimitry Andric   case KEY_DL:
6032480093f4SDimitry Andric     return "delete-line";
6033480093f4SDimitry Andric   case KEY_IL:
6034480093f4SDimitry Andric     return "insert-line";
6035480093f4SDimitry Andric   case KEY_DC:
6036480093f4SDimitry Andric     return "delete-char";
6037480093f4SDimitry Andric   case KEY_IC:
6038480093f4SDimitry Andric     return "insert-char";
6039480093f4SDimitry Andric   case KEY_CLEAR:
6040480093f4SDimitry Andric     return "clear";
6041480093f4SDimitry Andric   case KEY_EOS:
6042480093f4SDimitry Andric     return "clear-to-eos";
6043480093f4SDimitry Andric   case KEY_EOL:
6044480093f4SDimitry Andric     return "clear-to-eol";
6045480093f4SDimitry Andric   case KEY_SF:
6046480093f4SDimitry Andric     return "scroll-forward";
6047480093f4SDimitry Andric   case KEY_SR:
6048480093f4SDimitry Andric     return "scroll-backward";
6049480093f4SDimitry Andric   case KEY_NPAGE:
6050480093f4SDimitry Andric     return "page-down";
6051480093f4SDimitry Andric   case KEY_PPAGE:
6052480093f4SDimitry Andric     return "page-up";
6053480093f4SDimitry Andric   case KEY_STAB:
6054480093f4SDimitry Andric     return "set-tab";
6055480093f4SDimitry Andric   case KEY_CTAB:
6056480093f4SDimitry Andric     return "clear-tab";
6057480093f4SDimitry Andric   case KEY_CATAB:
6058480093f4SDimitry Andric     return "clear-all-tabs";
6059480093f4SDimitry Andric   case KEY_ENTER:
6060480093f4SDimitry Andric     return "enter";
6061480093f4SDimitry Andric   case KEY_PRINT:
6062480093f4SDimitry Andric     return "print";
6063480093f4SDimitry Andric   case KEY_LL:
6064480093f4SDimitry Andric     return "lower-left key";
6065480093f4SDimitry Andric   case KEY_A1:
6066480093f4SDimitry Andric     return "upper left of keypad";
6067480093f4SDimitry Andric   case KEY_A3:
6068480093f4SDimitry Andric     return "upper right of keypad";
6069480093f4SDimitry Andric   case KEY_B2:
6070480093f4SDimitry Andric     return "center of keypad";
6071480093f4SDimitry Andric   case KEY_C1:
6072480093f4SDimitry Andric     return "lower left of keypad";
6073480093f4SDimitry Andric   case KEY_C3:
6074480093f4SDimitry Andric     return "lower right of keypad";
6075480093f4SDimitry Andric   case KEY_BTAB:
6076480093f4SDimitry Andric     return "back-tab key";
6077480093f4SDimitry Andric   case KEY_BEG:
6078480093f4SDimitry Andric     return "begin key";
6079480093f4SDimitry Andric   case KEY_CANCEL:
6080480093f4SDimitry Andric     return "cancel key";
6081480093f4SDimitry Andric   case KEY_CLOSE:
6082480093f4SDimitry Andric     return "close key";
6083480093f4SDimitry Andric   case KEY_COMMAND:
6084480093f4SDimitry Andric     return "command key";
6085480093f4SDimitry Andric   case KEY_COPY:
6086480093f4SDimitry Andric     return "copy key";
6087480093f4SDimitry Andric   case KEY_CREATE:
6088480093f4SDimitry Andric     return "create key";
6089480093f4SDimitry Andric   case KEY_END:
6090480093f4SDimitry Andric     return "end key";
6091480093f4SDimitry Andric   case KEY_EXIT:
6092480093f4SDimitry Andric     return "exit key";
6093480093f4SDimitry Andric   case KEY_FIND:
6094480093f4SDimitry Andric     return "find key";
6095480093f4SDimitry Andric   case KEY_HELP:
6096480093f4SDimitry Andric     return "help key";
6097480093f4SDimitry Andric   case KEY_MARK:
6098480093f4SDimitry Andric     return "mark key";
6099480093f4SDimitry Andric   case KEY_MESSAGE:
6100480093f4SDimitry Andric     return "message key";
6101480093f4SDimitry Andric   case KEY_MOVE:
6102480093f4SDimitry Andric     return "move key";
6103480093f4SDimitry Andric   case KEY_NEXT:
6104480093f4SDimitry Andric     return "next key";
6105480093f4SDimitry Andric   case KEY_OPEN:
6106480093f4SDimitry Andric     return "open key";
6107480093f4SDimitry Andric   case KEY_OPTIONS:
6108480093f4SDimitry Andric     return "options key";
6109480093f4SDimitry Andric   case KEY_PREVIOUS:
6110480093f4SDimitry Andric     return "previous key";
6111480093f4SDimitry Andric   case KEY_REDO:
6112480093f4SDimitry Andric     return "redo key";
6113480093f4SDimitry Andric   case KEY_REFERENCE:
6114480093f4SDimitry Andric     return "reference key";
6115480093f4SDimitry Andric   case KEY_REFRESH:
6116480093f4SDimitry Andric     return "refresh key";
6117480093f4SDimitry Andric   case KEY_REPLACE:
6118480093f4SDimitry Andric     return "replace key";
6119480093f4SDimitry Andric   case KEY_RESTART:
6120480093f4SDimitry Andric     return "restart key";
6121480093f4SDimitry Andric   case KEY_RESUME:
6122480093f4SDimitry Andric     return "resume key";
6123480093f4SDimitry Andric   case KEY_SAVE:
6124480093f4SDimitry Andric     return "save key";
6125480093f4SDimitry Andric   case KEY_SBEG:
6126480093f4SDimitry Andric     return "shifted begin key";
6127480093f4SDimitry Andric   case KEY_SCANCEL:
6128480093f4SDimitry Andric     return "shifted cancel key";
6129480093f4SDimitry Andric   case KEY_SCOMMAND:
6130480093f4SDimitry Andric     return "shifted command key";
6131480093f4SDimitry Andric   case KEY_SCOPY:
6132480093f4SDimitry Andric     return "shifted copy key";
6133480093f4SDimitry Andric   case KEY_SCREATE:
6134480093f4SDimitry Andric     return "shifted create key";
6135480093f4SDimitry Andric   case KEY_SDC:
6136480093f4SDimitry Andric     return "shifted delete-character key";
6137480093f4SDimitry Andric   case KEY_SDL:
6138480093f4SDimitry Andric     return "shifted delete-line key";
6139480093f4SDimitry Andric   case KEY_SELECT:
6140480093f4SDimitry Andric     return "select key";
6141480093f4SDimitry Andric   case KEY_SEND:
6142480093f4SDimitry Andric     return "shifted end key";
6143480093f4SDimitry Andric   case KEY_SEOL:
6144480093f4SDimitry Andric     return "shifted clear-to-end-of-line key";
6145480093f4SDimitry Andric   case KEY_SEXIT:
6146480093f4SDimitry Andric     return "shifted exit key";
6147480093f4SDimitry Andric   case KEY_SFIND:
6148480093f4SDimitry Andric     return "shifted find key";
6149480093f4SDimitry Andric   case KEY_SHELP:
6150480093f4SDimitry Andric     return "shifted help key";
6151480093f4SDimitry Andric   case KEY_SHOME:
6152480093f4SDimitry Andric     return "shifted home key";
6153480093f4SDimitry Andric   case KEY_SIC:
6154480093f4SDimitry Andric     return "shifted insert-character key";
6155480093f4SDimitry Andric   case KEY_SLEFT:
6156480093f4SDimitry Andric     return "shifted left-arrow key";
6157480093f4SDimitry Andric   case KEY_SMESSAGE:
6158480093f4SDimitry Andric     return "shifted message key";
6159480093f4SDimitry Andric   case KEY_SMOVE:
6160480093f4SDimitry Andric     return "shifted move key";
6161480093f4SDimitry Andric   case KEY_SNEXT:
6162480093f4SDimitry Andric     return "shifted next key";
6163480093f4SDimitry Andric   case KEY_SOPTIONS:
6164480093f4SDimitry Andric     return "shifted options key";
6165480093f4SDimitry Andric   case KEY_SPREVIOUS:
6166480093f4SDimitry Andric     return "shifted previous key";
6167480093f4SDimitry Andric   case KEY_SPRINT:
6168480093f4SDimitry Andric     return "shifted print key";
6169480093f4SDimitry Andric   case KEY_SREDO:
6170480093f4SDimitry Andric     return "shifted redo key";
6171480093f4SDimitry Andric   case KEY_SREPLACE:
6172480093f4SDimitry Andric     return "shifted replace key";
6173480093f4SDimitry Andric   case KEY_SRIGHT:
6174480093f4SDimitry Andric     return "shifted right-arrow key";
6175480093f4SDimitry Andric   case KEY_SRSUME:
6176480093f4SDimitry Andric     return "shifted resume key";
6177480093f4SDimitry Andric   case KEY_SSAVE:
6178480093f4SDimitry Andric     return "shifted save key";
6179480093f4SDimitry Andric   case KEY_SSUSPEND:
6180480093f4SDimitry Andric     return "shifted suspend key";
6181480093f4SDimitry Andric   case KEY_SUNDO:
6182480093f4SDimitry Andric     return "shifted undo key";
6183480093f4SDimitry Andric   case KEY_SUSPEND:
6184480093f4SDimitry Andric     return "suspend key";
6185480093f4SDimitry Andric   case KEY_UNDO:
6186480093f4SDimitry Andric     return "undo key";
6187480093f4SDimitry Andric   case KEY_MOUSE:
6188480093f4SDimitry Andric     return "Mouse event has occurred";
6189480093f4SDimitry Andric   case KEY_RESIZE:
6190480093f4SDimitry Andric     return "Terminal resize event";
6191480093f4SDimitry Andric #ifdef KEY_EVENT
6192480093f4SDimitry Andric   case KEY_EVENT:
6193480093f4SDimitry Andric     return "We were interrupted by an event";
6194480093f4SDimitry Andric #endif
6195480093f4SDimitry Andric   case KEY_RETURN:
6196480093f4SDimitry Andric     return "return";
6197480093f4SDimitry Andric   case ' ':
6198480093f4SDimitry Andric     return "space";
6199480093f4SDimitry Andric   case '\t':
6200480093f4SDimitry Andric     return "tab";
6201480093f4SDimitry Andric   case KEY_ESCAPE:
6202480093f4SDimitry Andric     return "escape";
6203480093f4SDimitry Andric   default:
62045ffd83dbSDimitry Andric     if (llvm::isPrint(ch))
6205480093f4SDimitry Andric       snprintf(g_desc, sizeof(g_desc), "%c", ch);
6206480093f4SDimitry Andric     else
6207480093f4SDimitry Andric       snprintf(g_desc, sizeof(g_desc), "\\x%2.2x", ch);
6208480093f4SDimitry Andric     return g_desc;
6209480093f4SDimitry Andric   }
6210480093f4SDimitry Andric   return nullptr;
6211480093f4SDimitry Andric }
6212480093f4SDimitry Andric 
HelpDialogDelegate(const char * text,KeyHelp * key_help_array)6213480093f4SDimitry Andric HelpDialogDelegate::HelpDialogDelegate(const char *text,
6214480093f4SDimitry Andric                                        KeyHelp *key_help_array)
621581ad6265SDimitry Andric     : m_text() {
6216480093f4SDimitry Andric   if (text && text[0]) {
6217480093f4SDimitry Andric     m_text.SplitIntoLines(text);
6218480093f4SDimitry Andric     m_text.AppendString("");
6219480093f4SDimitry Andric   }
6220480093f4SDimitry Andric   if (key_help_array) {
6221480093f4SDimitry Andric     for (KeyHelp *key = key_help_array; key->ch; ++key) {
6222480093f4SDimitry Andric       StreamString key_description;
6223480093f4SDimitry Andric       key_description.Printf("%10s - %s", CursesKeyToCString(key->ch),
6224480093f4SDimitry Andric                              key->description);
6225480093f4SDimitry Andric       m_text.AppendString(key_description.GetString());
6226480093f4SDimitry Andric     }
6227480093f4SDimitry Andric   }
6228480093f4SDimitry Andric }
6229480093f4SDimitry Andric 
6230480093f4SDimitry Andric HelpDialogDelegate::~HelpDialogDelegate() = default;
6231480093f4SDimitry Andric 
WindowDelegateDraw(Window & window,bool force)6232480093f4SDimitry Andric bool HelpDialogDelegate::WindowDelegateDraw(Window &window, bool force) {
6233480093f4SDimitry Andric   window.Erase();
6234480093f4SDimitry Andric   const int window_height = window.GetHeight();
6235480093f4SDimitry Andric   int x = 2;
6236480093f4SDimitry Andric   int y = 1;
6237480093f4SDimitry Andric   const int min_y = y;
6238480093f4SDimitry Andric   const int max_y = window_height - 1 - y;
6239480093f4SDimitry Andric   const size_t num_visible_lines = max_y - min_y + 1;
6240480093f4SDimitry Andric   const size_t num_lines = m_text.GetSize();
6241480093f4SDimitry Andric   const char *bottom_message;
6242480093f4SDimitry Andric   if (num_lines <= num_visible_lines)
6243480093f4SDimitry Andric     bottom_message = "Press any key to exit";
6244480093f4SDimitry Andric   else
6245480093f4SDimitry Andric     bottom_message = "Use arrows to scroll, any other key to exit";
6246480093f4SDimitry Andric   window.DrawTitleBox(window.GetName(), bottom_message);
6247480093f4SDimitry Andric   while (y <= max_y) {
6248480093f4SDimitry Andric     window.MoveCursor(x, y);
6249480093f4SDimitry Andric     window.PutCStringTruncated(
6250e8d8bef9SDimitry Andric         1, m_text.GetStringAtIndex(m_first_visible_line + y - min_y));
6251480093f4SDimitry Andric     ++y;
6252480093f4SDimitry Andric   }
6253480093f4SDimitry Andric   return true;
6254480093f4SDimitry Andric }
6255480093f4SDimitry Andric 
WindowDelegateHandleChar(Window & window,int key)6256480093f4SDimitry Andric HandleCharResult HelpDialogDelegate::WindowDelegateHandleChar(Window &window,
6257480093f4SDimitry Andric                                                               int key) {
6258480093f4SDimitry Andric   bool done = false;
6259480093f4SDimitry Andric   const size_t num_lines = m_text.GetSize();
6260480093f4SDimitry Andric   const size_t num_visible_lines = window.GetHeight() - 2;
6261480093f4SDimitry Andric 
6262480093f4SDimitry Andric   if (num_lines <= num_visible_lines) {
6263480093f4SDimitry Andric     done = true;
6264480093f4SDimitry Andric     // If we have all lines visible and don't need scrolling, then any key
6265480093f4SDimitry Andric     // press will cause us to exit
6266480093f4SDimitry Andric   } else {
6267480093f4SDimitry Andric     switch (key) {
6268480093f4SDimitry Andric     case KEY_UP:
6269480093f4SDimitry Andric       if (m_first_visible_line > 0)
6270480093f4SDimitry Andric         --m_first_visible_line;
6271480093f4SDimitry Andric       break;
6272480093f4SDimitry Andric 
6273480093f4SDimitry Andric     case KEY_DOWN:
6274480093f4SDimitry Andric       if (m_first_visible_line + num_visible_lines < num_lines)
6275480093f4SDimitry Andric         ++m_first_visible_line;
6276480093f4SDimitry Andric       break;
6277480093f4SDimitry Andric 
6278480093f4SDimitry Andric     case KEY_PPAGE:
6279480093f4SDimitry Andric     case ',':
6280480093f4SDimitry Andric       if (m_first_visible_line > 0) {
6281480093f4SDimitry Andric         if (static_cast<size_t>(m_first_visible_line) >= num_visible_lines)
6282480093f4SDimitry Andric           m_first_visible_line -= num_visible_lines;
6283480093f4SDimitry Andric         else
6284480093f4SDimitry Andric           m_first_visible_line = 0;
6285480093f4SDimitry Andric       }
6286480093f4SDimitry Andric       break;
6287480093f4SDimitry Andric 
6288480093f4SDimitry Andric     case KEY_NPAGE:
6289480093f4SDimitry Andric     case '.':
6290480093f4SDimitry Andric       if (m_first_visible_line + num_visible_lines < num_lines) {
6291480093f4SDimitry Andric         m_first_visible_line += num_visible_lines;
6292480093f4SDimitry Andric         if (static_cast<size_t>(m_first_visible_line) > num_lines)
6293480093f4SDimitry Andric           m_first_visible_line = num_lines - num_visible_lines;
6294480093f4SDimitry Andric       }
6295480093f4SDimitry Andric       break;
6296480093f4SDimitry Andric 
6297480093f4SDimitry Andric     default:
6298480093f4SDimitry Andric       done = true;
6299480093f4SDimitry Andric       break;
6300480093f4SDimitry Andric     }
6301480093f4SDimitry Andric   }
6302480093f4SDimitry Andric   if (done)
6303480093f4SDimitry Andric     window.GetParent()->RemoveSubWindow(&window);
6304480093f4SDimitry Andric   return eKeyHandled;
6305480093f4SDimitry Andric }
6306480093f4SDimitry Andric 
6307480093f4SDimitry Andric class ApplicationDelegate : public WindowDelegate, public MenuDelegate {
6308480093f4SDimitry Andric public:
6309480093f4SDimitry Andric   enum {
6310480093f4SDimitry Andric     eMenuID_LLDB = 1,
6311480093f4SDimitry Andric     eMenuID_LLDBAbout,
6312480093f4SDimitry Andric     eMenuID_LLDBExit,
6313480093f4SDimitry Andric 
6314480093f4SDimitry Andric     eMenuID_Target,
6315480093f4SDimitry Andric     eMenuID_TargetCreate,
6316480093f4SDimitry Andric     eMenuID_TargetDelete,
6317480093f4SDimitry Andric 
6318480093f4SDimitry Andric     eMenuID_Process,
6319480093f4SDimitry Andric     eMenuID_ProcessAttach,
6320e8d8bef9SDimitry Andric     eMenuID_ProcessDetachResume,
6321e8d8bef9SDimitry Andric     eMenuID_ProcessDetachSuspended,
6322480093f4SDimitry Andric     eMenuID_ProcessLaunch,
6323480093f4SDimitry Andric     eMenuID_ProcessContinue,
6324480093f4SDimitry Andric     eMenuID_ProcessHalt,
6325480093f4SDimitry Andric     eMenuID_ProcessKill,
6326480093f4SDimitry Andric 
6327480093f4SDimitry Andric     eMenuID_Thread,
6328480093f4SDimitry Andric     eMenuID_ThreadStepIn,
6329480093f4SDimitry Andric     eMenuID_ThreadStepOver,
6330480093f4SDimitry Andric     eMenuID_ThreadStepOut,
6331480093f4SDimitry Andric 
6332480093f4SDimitry Andric     eMenuID_View,
6333480093f4SDimitry Andric     eMenuID_ViewBacktrace,
6334480093f4SDimitry Andric     eMenuID_ViewRegisters,
6335480093f4SDimitry Andric     eMenuID_ViewSource,
6336480093f4SDimitry Andric     eMenuID_ViewVariables,
6337349cc55cSDimitry Andric     eMenuID_ViewBreakpoints,
6338480093f4SDimitry Andric 
6339480093f4SDimitry Andric     eMenuID_Help,
6340480093f4SDimitry Andric     eMenuID_HelpGUIHelp
6341480093f4SDimitry Andric   };
6342480093f4SDimitry Andric 
ApplicationDelegate(Application & app,Debugger & debugger)6343480093f4SDimitry Andric   ApplicationDelegate(Application &app, Debugger &debugger)
6344480093f4SDimitry Andric       : WindowDelegate(), MenuDelegate(), m_app(app), m_debugger(debugger) {}
6345480093f4SDimitry Andric 
6346480093f4SDimitry Andric   ~ApplicationDelegate() override = default;
6347480093f4SDimitry Andric 
WindowDelegateDraw(Window & window,bool force)6348480093f4SDimitry Andric   bool WindowDelegateDraw(Window &window, bool force) override {
6349480093f4SDimitry Andric     return false; // Drawing not handled, let standard window drawing happen
6350480093f4SDimitry Andric   }
6351480093f4SDimitry Andric 
WindowDelegateHandleChar(Window & window,int key)6352480093f4SDimitry Andric   HandleCharResult WindowDelegateHandleChar(Window &window, int key) override {
6353480093f4SDimitry Andric     switch (key) {
6354480093f4SDimitry Andric     case '\t':
6355480093f4SDimitry Andric       window.SelectNextWindowAsActive();
6356480093f4SDimitry Andric       return eKeyHandled;
6357480093f4SDimitry Andric 
6358fe6060f1SDimitry Andric     case KEY_SHIFT_TAB:
6359e8d8bef9SDimitry Andric       window.SelectPreviousWindowAsActive();
6360e8d8bef9SDimitry Andric       return eKeyHandled;
6361e8d8bef9SDimitry Andric 
6362480093f4SDimitry Andric     case 'h':
6363480093f4SDimitry Andric       window.CreateHelpSubwindow();
6364480093f4SDimitry Andric       return eKeyHandled;
6365480093f4SDimitry Andric 
6366480093f4SDimitry Andric     case KEY_ESCAPE:
6367480093f4SDimitry Andric       return eQuitApplication;
6368480093f4SDimitry Andric 
6369480093f4SDimitry Andric     default:
6370480093f4SDimitry Andric       break;
6371480093f4SDimitry Andric     }
6372480093f4SDimitry Andric     return eKeyNotHandled;
6373480093f4SDimitry Andric   }
6374480093f4SDimitry Andric 
WindowDelegateGetHelpText()6375480093f4SDimitry Andric   const char *WindowDelegateGetHelpText() override {
6376480093f4SDimitry Andric     return "Welcome to the LLDB curses GUI.\n\n"
6377480093f4SDimitry Andric            "Press the TAB key to change the selected view.\n"
6378480093f4SDimitry Andric            "Each view has its own keyboard shortcuts, press 'h' to open a "
6379480093f4SDimitry Andric            "dialog to display them.\n\n"
6380480093f4SDimitry Andric            "Common key bindings for all views:";
6381480093f4SDimitry Andric   }
6382480093f4SDimitry Andric 
WindowDelegateGetKeyHelp()6383480093f4SDimitry Andric   KeyHelp *WindowDelegateGetKeyHelp() override {
6384480093f4SDimitry Andric     static curses::KeyHelp g_source_view_key_help[] = {
6385480093f4SDimitry Andric         {'\t', "Select next view"},
6386e8d8bef9SDimitry Andric         {KEY_BTAB, "Select previous view"},
6387480093f4SDimitry Andric         {'h', "Show help dialog with view specific key bindings"},
6388480093f4SDimitry Andric         {',', "Page up"},
6389480093f4SDimitry Andric         {'.', "Page down"},
6390480093f4SDimitry Andric         {KEY_UP, "Select previous"},
6391480093f4SDimitry Andric         {KEY_DOWN, "Select next"},
6392480093f4SDimitry Andric         {KEY_LEFT, "Unexpand or select parent"},
6393480093f4SDimitry Andric         {KEY_RIGHT, "Expand"},
6394480093f4SDimitry Andric         {KEY_PPAGE, "Page up"},
6395480093f4SDimitry Andric         {KEY_NPAGE, "Page down"},
6396480093f4SDimitry Andric         {'\0', nullptr}};
6397480093f4SDimitry Andric     return g_source_view_key_help;
6398480093f4SDimitry Andric   }
6399480093f4SDimitry Andric 
MenuDelegateAction(Menu & menu)6400480093f4SDimitry Andric   MenuActionResult MenuDelegateAction(Menu &menu) override {
6401480093f4SDimitry Andric     switch (menu.GetIdentifier()) {
6402349cc55cSDimitry Andric     case eMenuID_TargetCreate: {
6403349cc55cSDimitry Andric       WindowSP main_window_sp = m_app.GetMainWindow();
6404349cc55cSDimitry Andric       FormDelegateSP form_delegate_sp =
6405349cc55cSDimitry Andric           FormDelegateSP(new TargetCreateFormDelegate(m_debugger));
6406349cc55cSDimitry Andric       Rect bounds = main_window_sp->GetCenteredRect(80, 19);
6407349cc55cSDimitry Andric       WindowSP form_window_sp = main_window_sp->CreateSubWindow(
6408349cc55cSDimitry Andric           form_delegate_sp->GetName().c_str(), bounds, true);
6409349cc55cSDimitry Andric       WindowDelegateSP window_delegate_sp =
6410349cc55cSDimitry Andric           WindowDelegateSP(new FormWindowDelegate(form_delegate_sp));
6411349cc55cSDimitry Andric       form_window_sp->SetDelegate(window_delegate_sp);
6412349cc55cSDimitry Andric       return MenuActionResult::Handled;
6413349cc55cSDimitry Andric     }
6414480093f4SDimitry Andric     case eMenuID_ThreadStepIn: {
6415480093f4SDimitry Andric       ExecutionContext exe_ctx =
6416480093f4SDimitry Andric           m_debugger.GetCommandInterpreter().GetExecutionContext();
6417480093f4SDimitry Andric       if (exe_ctx.HasThreadScope()) {
6418480093f4SDimitry Andric         Process *process = exe_ctx.GetProcessPtr();
6419480093f4SDimitry Andric         if (process && process->IsAlive() &&
6420480093f4SDimitry Andric             StateIsStoppedState(process->GetState(), true))
6421480093f4SDimitry Andric           exe_ctx.GetThreadRef().StepIn(true);
6422480093f4SDimitry Andric       }
6423480093f4SDimitry Andric     }
6424480093f4SDimitry Andric       return MenuActionResult::Handled;
6425480093f4SDimitry Andric 
6426480093f4SDimitry Andric     case eMenuID_ThreadStepOut: {
6427480093f4SDimitry Andric       ExecutionContext exe_ctx =
6428480093f4SDimitry Andric           m_debugger.GetCommandInterpreter().GetExecutionContext();
6429480093f4SDimitry Andric       if (exe_ctx.HasThreadScope()) {
6430480093f4SDimitry Andric         Process *process = exe_ctx.GetProcessPtr();
6431480093f4SDimitry Andric         if (process && process->IsAlive() &&
643281ad6265SDimitry Andric             StateIsStoppedState(process->GetState(), true)) {
643381ad6265SDimitry Andric           Thread *thread = exe_ctx.GetThreadPtr();
643406c3fb27SDimitry Andric           uint32_t frame_idx =
643506c3fb27SDimitry Andric               thread->GetSelectedFrameIndex(SelectMostRelevantFrame);
643681ad6265SDimitry Andric           exe_ctx.GetThreadRef().StepOut(frame_idx);
643781ad6265SDimitry Andric         }
6438480093f4SDimitry Andric       }
6439480093f4SDimitry Andric     }
6440480093f4SDimitry Andric       return MenuActionResult::Handled;
6441480093f4SDimitry Andric 
6442480093f4SDimitry Andric     case eMenuID_ThreadStepOver: {
6443480093f4SDimitry Andric       ExecutionContext exe_ctx =
6444480093f4SDimitry Andric           m_debugger.GetCommandInterpreter().GetExecutionContext();
6445480093f4SDimitry Andric       if (exe_ctx.HasThreadScope()) {
6446480093f4SDimitry Andric         Process *process = exe_ctx.GetProcessPtr();
6447480093f4SDimitry Andric         if (process && process->IsAlive() &&
6448480093f4SDimitry Andric             StateIsStoppedState(process->GetState(), true))
6449480093f4SDimitry Andric           exe_ctx.GetThreadRef().StepOver(true);
6450480093f4SDimitry Andric       }
6451480093f4SDimitry Andric     }
6452480093f4SDimitry Andric       return MenuActionResult::Handled;
6453480093f4SDimitry Andric 
6454fe6060f1SDimitry Andric     case eMenuID_ProcessAttach: {
6455fe6060f1SDimitry Andric       WindowSP main_window_sp = m_app.GetMainWindow();
6456fe6060f1SDimitry Andric       FormDelegateSP form_delegate_sp = FormDelegateSP(
6457fe6060f1SDimitry Andric           new ProcessAttachFormDelegate(m_debugger, main_window_sp));
6458fe6060f1SDimitry Andric       Rect bounds = main_window_sp->GetCenteredRect(80, 22);
6459fe6060f1SDimitry Andric       WindowSP form_window_sp = main_window_sp->CreateSubWindow(
6460fe6060f1SDimitry Andric           form_delegate_sp->GetName().c_str(), bounds, true);
6461fe6060f1SDimitry Andric       WindowDelegateSP window_delegate_sp =
6462fe6060f1SDimitry Andric           WindowDelegateSP(new FormWindowDelegate(form_delegate_sp));
6463fe6060f1SDimitry Andric       form_window_sp->SetDelegate(window_delegate_sp);
6464fe6060f1SDimitry Andric       return MenuActionResult::Handled;
6465fe6060f1SDimitry Andric     }
6466349cc55cSDimitry Andric     case eMenuID_ProcessLaunch: {
6467349cc55cSDimitry Andric       WindowSP main_window_sp = m_app.GetMainWindow();
6468349cc55cSDimitry Andric       FormDelegateSP form_delegate_sp = FormDelegateSP(
6469349cc55cSDimitry Andric           new ProcessLaunchFormDelegate(m_debugger, main_window_sp));
6470349cc55cSDimitry Andric       Rect bounds = main_window_sp->GetCenteredRect(80, 22);
6471349cc55cSDimitry Andric       WindowSP form_window_sp = main_window_sp->CreateSubWindow(
6472349cc55cSDimitry Andric           form_delegate_sp->GetName().c_str(), bounds, true);
6473349cc55cSDimitry Andric       WindowDelegateSP window_delegate_sp =
6474349cc55cSDimitry Andric           WindowDelegateSP(new FormWindowDelegate(form_delegate_sp));
6475349cc55cSDimitry Andric       form_window_sp->SetDelegate(window_delegate_sp);
6476349cc55cSDimitry Andric       return MenuActionResult::Handled;
6477349cc55cSDimitry Andric     }
6478fe6060f1SDimitry Andric 
6479480093f4SDimitry Andric     case eMenuID_ProcessContinue: {
6480480093f4SDimitry Andric       ExecutionContext exe_ctx =
6481480093f4SDimitry Andric           m_debugger.GetCommandInterpreter().GetExecutionContext();
6482480093f4SDimitry Andric       if (exe_ctx.HasProcessScope()) {
6483480093f4SDimitry Andric         Process *process = exe_ctx.GetProcessPtr();
6484480093f4SDimitry Andric         if (process && process->IsAlive() &&
6485480093f4SDimitry Andric             StateIsStoppedState(process->GetState(), true))
6486480093f4SDimitry Andric           process->Resume();
6487480093f4SDimitry Andric       }
6488480093f4SDimitry Andric     }
6489480093f4SDimitry Andric       return MenuActionResult::Handled;
6490480093f4SDimitry Andric 
6491480093f4SDimitry Andric     case eMenuID_ProcessKill: {
6492480093f4SDimitry Andric       ExecutionContext exe_ctx =
6493480093f4SDimitry Andric           m_debugger.GetCommandInterpreter().GetExecutionContext();
6494480093f4SDimitry Andric       if (exe_ctx.HasProcessScope()) {
6495480093f4SDimitry Andric         Process *process = exe_ctx.GetProcessPtr();
6496480093f4SDimitry Andric         if (process && process->IsAlive())
6497480093f4SDimitry Andric           process->Destroy(false);
6498480093f4SDimitry Andric       }
6499480093f4SDimitry Andric     }
6500480093f4SDimitry Andric       return MenuActionResult::Handled;
6501480093f4SDimitry Andric 
6502480093f4SDimitry Andric     case eMenuID_ProcessHalt: {
6503480093f4SDimitry Andric       ExecutionContext exe_ctx =
6504480093f4SDimitry Andric           m_debugger.GetCommandInterpreter().GetExecutionContext();
6505480093f4SDimitry Andric       if (exe_ctx.HasProcessScope()) {
6506480093f4SDimitry Andric         Process *process = exe_ctx.GetProcessPtr();
6507480093f4SDimitry Andric         if (process && process->IsAlive())
6508480093f4SDimitry Andric           process->Halt();
6509480093f4SDimitry Andric       }
6510480093f4SDimitry Andric     }
6511480093f4SDimitry Andric       return MenuActionResult::Handled;
6512480093f4SDimitry Andric 
6513e8d8bef9SDimitry Andric     case eMenuID_ProcessDetachResume:
6514e8d8bef9SDimitry Andric     case eMenuID_ProcessDetachSuspended: {
6515480093f4SDimitry Andric       ExecutionContext exe_ctx =
6516480093f4SDimitry Andric           m_debugger.GetCommandInterpreter().GetExecutionContext();
6517480093f4SDimitry Andric       if (exe_ctx.HasProcessScope()) {
6518480093f4SDimitry Andric         Process *process = exe_ctx.GetProcessPtr();
6519480093f4SDimitry Andric         if (process && process->IsAlive())
6520e8d8bef9SDimitry Andric           process->Detach(menu.GetIdentifier() ==
6521e8d8bef9SDimitry Andric                           eMenuID_ProcessDetachSuspended);
6522480093f4SDimitry Andric       }
6523480093f4SDimitry Andric     }
6524480093f4SDimitry Andric       return MenuActionResult::Handled;
6525480093f4SDimitry Andric 
6526480093f4SDimitry Andric     case eMenuID_Process: {
6527480093f4SDimitry Andric       // Populate the menu with all of the threads if the process is stopped
6528480093f4SDimitry Andric       // when the Process menu gets selected and is about to display its
6529480093f4SDimitry Andric       // submenu.
6530480093f4SDimitry Andric       Menus &submenus = menu.GetSubmenus();
6531480093f4SDimitry Andric       ExecutionContext exe_ctx =
6532480093f4SDimitry Andric           m_debugger.GetCommandInterpreter().GetExecutionContext();
6533480093f4SDimitry Andric       Process *process = exe_ctx.GetProcessPtr();
6534480093f4SDimitry Andric       if (process && process->IsAlive() &&
6535480093f4SDimitry Andric           StateIsStoppedState(process->GetState(), true)) {
6536480093f4SDimitry Andric         if (submenus.size() == 7)
6537480093f4SDimitry Andric           menu.AddSubmenu(MenuSP(new Menu(Menu::Type::Separator)));
6538480093f4SDimitry Andric         else if (submenus.size() > 8)
6539480093f4SDimitry Andric           submenus.erase(submenus.begin() + 8, submenus.end());
6540480093f4SDimitry Andric 
6541480093f4SDimitry Andric         ThreadList &threads = process->GetThreadList();
6542480093f4SDimitry Andric         std::lock_guard<std::recursive_mutex> guard(threads.GetMutex());
6543480093f4SDimitry Andric         size_t num_threads = threads.GetSize();
6544480093f4SDimitry Andric         for (size_t i = 0; i < num_threads; ++i) {
6545480093f4SDimitry Andric           ThreadSP thread_sp = threads.GetThreadAtIndex(i);
6546480093f4SDimitry Andric           char menu_char = '\0';
6547480093f4SDimitry Andric           if (i < 9)
6548480093f4SDimitry Andric             menu_char = '1' + i;
6549480093f4SDimitry Andric           StreamString thread_menu_title;
6550480093f4SDimitry Andric           thread_menu_title.Printf("Thread %u", thread_sp->GetIndexID());
6551480093f4SDimitry Andric           const char *thread_name = thread_sp->GetName();
6552480093f4SDimitry Andric           if (thread_name && thread_name[0])
6553480093f4SDimitry Andric             thread_menu_title.Printf(" %s", thread_name);
6554480093f4SDimitry Andric           else {
6555480093f4SDimitry Andric             const char *queue_name = thread_sp->GetQueueName();
6556480093f4SDimitry Andric             if (queue_name && queue_name[0])
6557480093f4SDimitry Andric               thread_menu_title.Printf(" %s", queue_name);
6558480093f4SDimitry Andric           }
6559480093f4SDimitry Andric           menu.AddSubmenu(
6560480093f4SDimitry Andric               MenuSP(new Menu(thread_menu_title.GetString().str().c_str(),
6561480093f4SDimitry Andric                               nullptr, menu_char, thread_sp->GetID())));
6562480093f4SDimitry Andric         }
6563480093f4SDimitry Andric       } else if (submenus.size() > 7) {
6564480093f4SDimitry Andric         // Remove the separator and any other thread submenu items that were
6565480093f4SDimitry Andric         // previously added
6566480093f4SDimitry Andric         submenus.erase(submenus.begin() + 7, submenus.end());
6567480093f4SDimitry Andric       }
6568349cc55cSDimitry Andric       // Since we are adding and removing items we need to recalculate the
6569349cc55cSDimitry Andric       // name lengths
6570480093f4SDimitry Andric       menu.RecalculateNameLengths();
6571480093f4SDimitry Andric     }
6572480093f4SDimitry Andric       return MenuActionResult::Handled;
6573480093f4SDimitry Andric 
6574480093f4SDimitry Andric     case eMenuID_ViewVariables: {
6575480093f4SDimitry Andric       WindowSP main_window_sp = m_app.GetMainWindow();
6576480093f4SDimitry Andric       WindowSP source_window_sp = main_window_sp->FindSubWindow("Source");
6577480093f4SDimitry Andric       WindowSP variables_window_sp = main_window_sp->FindSubWindow("Variables");
6578480093f4SDimitry Andric       WindowSP registers_window_sp = main_window_sp->FindSubWindow("Registers");
6579480093f4SDimitry Andric       const Rect source_bounds = source_window_sp->GetBounds();
6580480093f4SDimitry Andric 
6581480093f4SDimitry Andric       if (variables_window_sp) {
6582480093f4SDimitry Andric         const Rect variables_bounds = variables_window_sp->GetBounds();
6583480093f4SDimitry Andric 
6584480093f4SDimitry Andric         main_window_sp->RemoveSubWindow(variables_window_sp.get());
6585480093f4SDimitry Andric 
6586480093f4SDimitry Andric         if (registers_window_sp) {
6587480093f4SDimitry Andric           // We have a registers window, so give all the area back to the
6588480093f4SDimitry Andric           // registers window
6589480093f4SDimitry Andric           Rect registers_bounds = variables_bounds;
6590480093f4SDimitry Andric           registers_bounds.size.width = source_bounds.size.width;
6591480093f4SDimitry Andric           registers_window_sp->SetBounds(registers_bounds);
6592480093f4SDimitry Andric         } else {
6593480093f4SDimitry Andric           // We have no registers window showing so give the bottom area back
6594480093f4SDimitry Andric           // to the source view
6595480093f4SDimitry Andric           source_window_sp->Resize(source_bounds.size.width,
6596480093f4SDimitry Andric                                    source_bounds.size.height +
6597480093f4SDimitry Andric                                        variables_bounds.size.height);
6598480093f4SDimitry Andric         }
6599480093f4SDimitry Andric       } else {
6600480093f4SDimitry Andric         Rect new_variables_rect;
6601480093f4SDimitry Andric         if (registers_window_sp) {
6602480093f4SDimitry Andric           // We have a registers window so split the area of the registers
6603480093f4SDimitry Andric           // window into two columns where the left hand side will be the
6604480093f4SDimitry Andric           // variables and the right hand side will be the registers
6605480093f4SDimitry Andric           const Rect variables_bounds = registers_window_sp->GetBounds();
6606480093f4SDimitry Andric           Rect new_registers_rect;
6607480093f4SDimitry Andric           variables_bounds.VerticalSplitPercentage(0.50, new_variables_rect,
6608480093f4SDimitry Andric                                                    new_registers_rect);
6609480093f4SDimitry Andric           registers_window_sp->SetBounds(new_registers_rect);
6610480093f4SDimitry Andric         } else {
6611e8d8bef9SDimitry Andric           // No registers window, grab the bottom part of the source window
6612480093f4SDimitry Andric           Rect new_source_rect;
6613480093f4SDimitry Andric           source_bounds.HorizontalSplitPercentage(0.70, new_source_rect,
6614480093f4SDimitry Andric                                                   new_variables_rect);
6615480093f4SDimitry Andric           source_window_sp->SetBounds(new_source_rect);
6616480093f4SDimitry Andric         }
6617480093f4SDimitry Andric         WindowSP new_window_sp = main_window_sp->CreateSubWindow(
6618480093f4SDimitry Andric             "Variables", new_variables_rect, false);
6619480093f4SDimitry Andric         new_window_sp->SetDelegate(
6620480093f4SDimitry Andric             WindowDelegateSP(new FrameVariablesWindowDelegate(m_debugger)));
6621480093f4SDimitry Andric       }
6622480093f4SDimitry Andric       touchwin(stdscr);
6623480093f4SDimitry Andric     }
6624480093f4SDimitry Andric       return MenuActionResult::Handled;
6625480093f4SDimitry Andric 
6626480093f4SDimitry Andric     case eMenuID_ViewRegisters: {
6627480093f4SDimitry Andric       WindowSP main_window_sp = m_app.GetMainWindow();
6628480093f4SDimitry Andric       WindowSP source_window_sp = main_window_sp->FindSubWindow("Source");
6629480093f4SDimitry Andric       WindowSP variables_window_sp = main_window_sp->FindSubWindow("Variables");
6630480093f4SDimitry Andric       WindowSP registers_window_sp = main_window_sp->FindSubWindow("Registers");
6631480093f4SDimitry Andric       const Rect source_bounds = source_window_sp->GetBounds();
6632480093f4SDimitry Andric 
6633480093f4SDimitry Andric       if (registers_window_sp) {
6634480093f4SDimitry Andric         if (variables_window_sp) {
6635480093f4SDimitry Andric           const Rect variables_bounds = variables_window_sp->GetBounds();
6636480093f4SDimitry Andric 
6637480093f4SDimitry Andric           // We have a variables window, so give all the area back to the
6638480093f4SDimitry Andric           // variables window
6639480093f4SDimitry Andric           variables_window_sp->Resize(variables_bounds.size.width +
6640480093f4SDimitry Andric                                           registers_window_sp->GetWidth(),
6641480093f4SDimitry Andric                                       variables_bounds.size.height);
6642480093f4SDimitry Andric         } else {
6643480093f4SDimitry Andric           // We have no variables window showing so give the bottom area back
6644480093f4SDimitry Andric           // to the source view
6645480093f4SDimitry Andric           source_window_sp->Resize(source_bounds.size.width,
6646480093f4SDimitry Andric                                    source_bounds.size.height +
6647480093f4SDimitry Andric                                        registers_window_sp->GetHeight());
6648480093f4SDimitry Andric         }
6649480093f4SDimitry Andric         main_window_sp->RemoveSubWindow(registers_window_sp.get());
6650480093f4SDimitry Andric       } else {
6651480093f4SDimitry Andric         Rect new_regs_rect;
6652480093f4SDimitry Andric         if (variables_window_sp) {
6653480093f4SDimitry Andric           // We have a variables window, split it into two columns where the
6654480093f4SDimitry Andric           // left hand side will be the variables and the right hand side will
6655480093f4SDimitry Andric           // be the registers
6656480093f4SDimitry Andric           const Rect variables_bounds = variables_window_sp->GetBounds();
6657480093f4SDimitry Andric           Rect new_vars_rect;
6658480093f4SDimitry Andric           variables_bounds.VerticalSplitPercentage(0.50, new_vars_rect,
6659480093f4SDimitry Andric                                                    new_regs_rect);
6660480093f4SDimitry Andric           variables_window_sp->SetBounds(new_vars_rect);
6661480093f4SDimitry Andric         } else {
6662e8d8bef9SDimitry Andric           // No variables window, grab the bottom part of the source window
6663480093f4SDimitry Andric           Rect new_source_rect;
6664480093f4SDimitry Andric           source_bounds.HorizontalSplitPercentage(0.70, new_source_rect,
6665480093f4SDimitry Andric                                                   new_regs_rect);
6666480093f4SDimitry Andric           source_window_sp->SetBounds(new_source_rect);
6667480093f4SDimitry Andric         }
6668480093f4SDimitry Andric         WindowSP new_window_sp =
6669480093f4SDimitry Andric             main_window_sp->CreateSubWindow("Registers", new_regs_rect, false);
6670480093f4SDimitry Andric         new_window_sp->SetDelegate(
6671480093f4SDimitry Andric             WindowDelegateSP(new RegistersWindowDelegate(m_debugger)));
6672480093f4SDimitry Andric       }
6673480093f4SDimitry Andric       touchwin(stdscr);
6674480093f4SDimitry Andric     }
6675480093f4SDimitry Andric       return MenuActionResult::Handled;
6676480093f4SDimitry Andric 
6677349cc55cSDimitry Andric     case eMenuID_ViewBreakpoints: {
6678349cc55cSDimitry Andric       WindowSP main_window_sp = m_app.GetMainWindow();
6679349cc55cSDimitry Andric       WindowSP threads_window_sp = main_window_sp->FindSubWindow("Threads");
6680349cc55cSDimitry Andric       WindowSP breakpoints_window_sp =
6681349cc55cSDimitry Andric           main_window_sp->FindSubWindow("Breakpoints");
6682349cc55cSDimitry Andric       const Rect threads_bounds = threads_window_sp->GetBounds();
6683349cc55cSDimitry Andric 
6684349cc55cSDimitry Andric       // If a breakpoints window already exists, remove it and give the area
6685349cc55cSDimitry Andric       // it used to occupy to the threads window. If it doesn't exist, split
6686349cc55cSDimitry Andric       // the threads window horizontally into two windows where the top window
6687349cc55cSDimitry Andric       // is the threads window and the bottom window is a newly added
6688349cc55cSDimitry Andric       // breakpoints window.
6689349cc55cSDimitry Andric       if (breakpoints_window_sp) {
6690349cc55cSDimitry Andric         threads_window_sp->Resize(threads_bounds.size.width,
6691349cc55cSDimitry Andric                                   threads_bounds.size.height +
6692349cc55cSDimitry Andric                                       breakpoints_window_sp->GetHeight());
6693349cc55cSDimitry Andric         main_window_sp->RemoveSubWindow(breakpoints_window_sp.get());
6694349cc55cSDimitry Andric       } else {
6695349cc55cSDimitry Andric         Rect new_threads_bounds, breakpoints_bounds;
6696349cc55cSDimitry Andric         threads_bounds.HorizontalSplitPercentage(0.70, new_threads_bounds,
6697349cc55cSDimitry Andric                                                  breakpoints_bounds);
6698349cc55cSDimitry Andric         threads_window_sp->SetBounds(new_threads_bounds);
6699349cc55cSDimitry Andric         breakpoints_window_sp = main_window_sp->CreateSubWindow(
6700349cc55cSDimitry Andric             "Breakpoints", breakpoints_bounds, false);
6701349cc55cSDimitry Andric         TreeDelegateSP breakpoints_delegate_sp(
6702349cc55cSDimitry Andric             new BreakpointsTreeDelegate(m_debugger));
6703349cc55cSDimitry Andric         breakpoints_window_sp->SetDelegate(WindowDelegateSP(
6704349cc55cSDimitry Andric             new TreeWindowDelegate(m_debugger, breakpoints_delegate_sp)));
6705349cc55cSDimitry Andric       }
6706349cc55cSDimitry Andric       touchwin(stdscr);
6707349cc55cSDimitry Andric       return MenuActionResult::Handled;
6708349cc55cSDimitry Andric     }
6709349cc55cSDimitry Andric 
6710480093f4SDimitry Andric     case eMenuID_HelpGUIHelp:
6711480093f4SDimitry Andric       m_app.GetMainWindow()->CreateHelpSubwindow();
6712480093f4SDimitry Andric       return MenuActionResult::Handled;
6713480093f4SDimitry Andric 
6714480093f4SDimitry Andric     default:
6715480093f4SDimitry Andric       break;
6716480093f4SDimitry Andric     }
6717480093f4SDimitry Andric 
6718480093f4SDimitry Andric     return MenuActionResult::NotHandled;
6719480093f4SDimitry Andric   }
6720480093f4SDimitry Andric 
6721480093f4SDimitry Andric protected:
6722480093f4SDimitry Andric   Application &m_app;
6723480093f4SDimitry Andric   Debugger &m_debugger;
6724480093f4SDimitry Andric };
6725480093f4SDimitry Andric 
6726480093f4SDimitry Andric class StatusBarWindowDelegate : public WindowDelegate {
6727480093f4SDimitry Andric public:
StatusBarWindowDelegate(Debugger & debugger)6728480093f4SDimitry Andric   StatusBarWindowDelegate(Debugger &debugger) : m_debugger(debugger) {
6729480093f4SDimitry Andric     FormatEntity::Parse("Thread: ${thread.id%tid}", m_format);
6730480093f4SDimitry Andric   }
6731480093f4SDimitry Andric 
6732480093f4SDimitry Andric   ~StatusBarWindowDelegate() override = default;
6733480093f4SDimitry Andric 
WindowDelegateDraw(Window & window,bool force)6734480093f4SDimitry Andric   bool WindowDelegateDraw(Window &window, bool force) override {
6735480093f4SDimitry Andric     ExecutionContext exe_ctx =
6736480093f4SDimitry Andric         m_debugger.GetCommandInterpreter().GetExecutionContext();
6737480093f4SDimitry Andric     Process *process = exe_ctx.GetProcessPtr();
6738480093f4SDimitry Andric     Thread *thread = exe_ctx.GetThreadPtr();
6739480093f4SDimitry Andric     StackFrame *frame = exe_ctx.GetFramePtr();
6740480093f4SDimitry Andric     window.Erase();
6741e8d8bef9SDimitry Andric     window.SetBackground(BlackOnWhite);
6742480093f4SDimitry Andric     window.MoveCursor(0, 0);
6743480093f4SDimitry Andric     if (process) {
6744480093f4SDimitry Andric       const StateType state = process->GetState();
6745480093f4SDimitry Andric       window.Printf("Process: %5" PRIu64 " %10s", process->GetID(),
6746480093f4SDimitry Andric                     StateAsCString(state));
6747480093f4SDimitry Andric 
6748480093f4SDimitry Andric       if (StateIsStoppedState(state, true)) {
6749480093f4SDimitry Andric         StreamString strm;
6750480093f4SDimitry Andric         if (thread && FormatEntity::Format(m_format, strm, nullptr, &exe_ctx,
6751480093f4SDimitry Andric                                            nullptr, nullptr, false, false)) {
6752480093f4SDimitry Andric           window.MoveCursor(40, 0);
6753e8d8bef9SDimitry Andric           window.PutCStringTruncated(1, strm.GetString().str().c_str());
6754480093f4SDimitry Andric         }
6755480093f4SDimitry Andric 
6756480093f4SDimitry Andric         window.MoveCursor(60, 0);
6757480093f4SDimitry Andric         if (frame)
6758480093f4SDimitry Andric           window.Printf("Frame: %3u  PC = 0x%16.16" PRIx64,
6759480093f4SDimitry Andric                         frame->GetFrameIndex(),
6760480093f4SDimitry Andric                         frame->GetFrameCodeAddress().GetOpcodeLoadAddress(
6761480093f4SDimitry Andric                             exe_ctx.GetTargetPtr()));
6762480093f4SDimitry Andric       } else if (state == eStateExited) {
6763480093f4SDimitry Andric         const char *exit_desc = process->GetExitDescription();
6764480093f4SDimitry Andric         const int exit_status = process->GetExitStatus();
6765480093f4SDimitry Andric         if (exit_desc && exit_desc[0])
6766480093f4SDimitry Andric           window.Printf(" with status = %i (%s)", exit_status, exit_desc);
6767480093f4SDimitry Andric         else
6768480093f4SDimitry Andric           window.Printf(" with status = %i", exit_status);
6769480093f4SDimitry Andric       }
6770480093f4SDimitry Andric     }
6771480093f4SDimitry Andric     return true;
6772480093f4SDimitry Andric   }
6773480093f4SDimitry Andric 
6774480093f4SDimitry Andric protected:
6775480093f4SDimitry Andric   Debugger &m_debugger;
6776480093f4SDimitry Andric   FormatEntity::Entry m_format;
6777480093f4SDimitry Andric };
6778480093f4SDimitry Andric 
6779480093f4SDimitry Andric class SourceFileWindowDelegate : public WindowDelegate {
6780480093f4SDimitry Andric public:
SourceFileWindowDelegate(Debugger & debugger)6781480093f4SDimitry Andric   SourceFileWindowDelegate(Debugger &debugger)
6782480093f4SDimitry Andric       : WindowDelegate(), m_debugger(debugger), m_sc(), m_file_sp(),
678381ad6265SDimitry Andric         m_disassembly_sp(), m_disassembly_range(), m_title() {}
6784480093f4SDimitry Andric 
6785480093f4SDimitry Andric   ~SourceFileWindowDelegate() override = default;
6786480093f4SDimitry Andric 
Update(const SymbolContext & sc)6787480093f4SDimitry Andric   void Update(const SymbolContext &sc) { m_sc = sc; }
6788480093f4SDimitry Andric 
NumVisibleLines() const6789480093f4SDimitry Andric   uint32_t NumVisibleLines() const { return m_max_y - m_min_y; }
6790480093f4SDimitry Andric 
WindowDelegateGetHelpText()6791480093f4SDimitry Andric   const char *WindowDelegateGetHelpText() override {
6792480093f4SDimitry Andric     return "Source/Disassembly window keyboard shortcuts:";
6793480093f4SDimitry Andric   }
6794480093f4SDimitry Andric 
WindowDelegateGetKeyHelp()6795480093f4SDimitry Andric   KeyHelp *WindowDelegateGetKeyHelp() override {
6796480093f4SDimitry Andric     static curses::KeyHelp g_source_view_key_help[] = {
6797480093f4SDimitry Andric         {KEY_RETURN, "Run to selected line with one shot breakpoint"},
6798480093f4SDimitry Andric         {KEY_UP, "Select previous source line"},
6799480093f4SDimitry Andric         {KEY_DOWN, "Select next source line"},
6800e8d8bef9SDimitry Andric         {KEY_LEFT, "Scroll to the left"},
6801e8d8bef9SDimitry Andric         {KEY_RIGHT, "Scroll to the right"},
6802480093f4SDimitry Andric         {KEY_PPAGE, "Page up"},
6803480093f4SDimitry Andric         {KEY_NPAGE, "Page down"},
6804480093f4SDimitry Andric         {'b', "Set breakpoint on selected source/disassembly line"},
6805480093f4SDimitry Andric         {'c', "Continue process"},
6806480093f4SDimitry Andric         {'D', "Detach with process suspended"},
6807480093f4SDimitry Andric         {'h', "Show help dialog"},
6808480093f4SDimitry Andric         {'n', "Step over (source line)"},
6809480093f4SDimitry Andric         {'N', "Step over (single instruction)"},
6810e8d8bef9SDimitry Andric         {'f', "Step out (finish)"},
6811480093f4SDimitry Andric         {'s', "Step in (source line)"},
6812480093f4SDimitry Andric         {'S', "Step in (single instruction)"},
6813e8d8bef9SDimitry Andric         {'u', "Frame up"},
6814e8d8bef9SDimitry Andric         {'d', "Frame down"},
6815480093f4SDimitry Andric         {',', "Page up"},
6816480093f4SDimitry Andric         {'.', "Page down"},
6817480093f4SDimitry Andric         {'\0', nullptr}};
6818480093f4SDimitry Andric     return g_source_view_key_help;
6819480093f4SDimitry Andric   }
6820480093f4SDimitry Andric 
WindowDelegateDraw(Window & window,bool force)6821480093f4SDimitry Andric   bool WindowDelegateDraw(Window &window, bool force) override {
6822480093f4SDimitry Andric     ExecutionContext exe_ctx =
6823480093f4SDimitry Andric         m_debugger.GetCommandInterpreter().GetExecutionContext();
6824480093f4SDimitry Andric     Process *process = exe_ctx.GetProcessPtr();
6825480093f4SDimitry Andric     Thread *thread = nullptr;
6826480093f4SDimitry Andric 
6827480093f4SDimitry Andric     bool update_location = false;
6828480093f4SDimitry Andric     if (process) {
6829480093f4SDimitry Andric       StateType state = process->GetState();
6830480093f4SDimitry Andric       if (StateIsStoppedState(state, true)) {
6831480093f4SDimitry Andric         // We are stopped, so it is ok to
6832480093f4SDimitry Andric         update_location = true;
6833480093f4SDimitry Andric       }
6834480093f4SDimitry Andric     }
6835480093f4SDimitry Andric 
6836480093f4SDimitry Andric     m_min_x = 1;
6837480093f4SDimitry Andric     m_min_y = 2;
6838480093f4SDimitry Andric     m_max_x = window.GetMaxX() - 1;
6839480093f4SDimitry Andric     m_max_y = window.GetMaxY() - 1;
6840480093f4SDimitry Andric 
6841480093f4SDimitry Andric     const uint32_t num_visible_lines = NumVisibleLines();
6842480093f4SDimitry Andric     StackFrameSP frame_sp;
6843480093f4SDimitry Andric     bool set_selected_line_to_pc = false;
6844480093f4SDimitry Andric 
6845480093f4SDimitry Andric     if (update_location) {
6846bdd1243dSDimitry Andric       const bool process_alive = process->IsAlive();
6847480093f4SDimitry Andric       bool thread_changed = false;
6848480093f4SDimitry Andric       if (process_alive) {
6849480093f4SDimitry Andric         thread = exe_ctx.GetThreadPtr();
6850480093f4SDimitry Andric         if (thread) {
685106c3fb27SDimitry Andric           frame_sp = thread->GetSelectedFrame(SelectMostRelevantFrame);
6852480093f4SDimitry Andric           auto tid = thread->GetID();
6853480093f4SDimitry Andric           thread_changed = tid != m_tid;
6854480093f4SDimitry Andric           m_tid = tid;
6855480093f4SDimitry Andric         } else {
6856480093f4SDimitry Andric           if (m_tid != LLDB_INVALID_THREAD_ID) {
6857480093f4SDimitry Andric             thread_changed = true;
6858480093f4SDimitry Andric             m_tid = LLDB_INVALID_THREAD_ID;
6859480093f4SDimitry Andric           }
6860480093f4SDimitry Andric         }
6861480093f4SDimitry Andric       }
6862480093f4SDimitry Andric       const uint32_t stop_id = process ? process->GetStopID() : 0;
6863480093f4SDimitry Andric       const bool stop_id_changed = stop_id != m_stop_id;
6864480093f4SDimitry Andric       bool frame_changed = false;
6865480093f4SDimitry Andric       m_stop_id = stop_id;
6866480093f4SDimitry Andric       m_title.Clear();
6867480093f4SDimitry Andric       if (frame_sp) {
6868480093f4SDimitry Andric         m_sc = frame_sp->GetSymbolContext(eSymbolContextEverything);
6869480093f4SDimitry Andric         if (m_sc.module_sp) {
6870480093f4SDimitry Andric           m_title.Printf(
6871480093f4SDimitry Andric               "%s", m_sc.module_sp->GetFileSpec().GetFilename().GetCString());
6872480093f4SDimitry Andric           ConstString func_name = m_sc.GetFunctionName();
6873480093f4SDimitry Andric           if (func_name)
6874480093f4SDimitry Andric             m_title.Printf("`%s", func_name.GetCString());
6875480093f4SDimitry Andric         }
6876480093f4SDimitry Andric         const uint32_t frame_idx = frame_sp->GetFrameIndex();
6877480093f4SDimitry Andric         frame_changed = frame_idx != m_frame_idx;
6878480093f4SDimitry Andric         m_frame_idx = frame_idx;
6879480093f4SDimitry Andric       } else {
6880480093f4SDimitry Andric         m_sc.Clear(true);
6881480093f4SDimitry Andric         frame_changed = m_frame_idx != UINT32_MAX;
6882480093f4SDimitry Andric         m_frame_idx = UINT32_MAX;
6883480093f4SDimitry Andric       }
6884480093f4SDimitry Andric 
6885480093f4SDimitry Andric       const bool context_changed =
6886480093f4SDimitry Andric           thread_changed || frame_changed || stop_id_changed;
6887480093f4SDimitry Andric 
6888480093f4SDimitry Andric       if (process_alive) {
6889480093f4SDimitry Andric         if (m_sc.line_entry.IsValid()) {
6890480093f4SDimitry Andric           m_pc_line = m_sc.line_entry.line;
6891480093f4SDimitry Andric           if (m_pc_line != UINT32_MAX)
6892480093f4SDimitry Andric             --m_pc_line; // Convert to zero based line number...
6893480093f4SDimitry Andric           // Update the selected line if the stop ID changed...
6894480093f4SDimitry Andric           if (context_changed)
6895480093f4SDimitry Andric             m_selected_line = m_pc_line;
6896480093f4SDimitry Andric 
6897480093f4SDimitry Andric           if (m_file_sp && m_file_sp->GetFileSpec() == m_sc.line_entry.file) {
6898349cc55cSDimitry Andric             // Same file, nothing to do, we should either have the lines or
6899349cc55cSDimitry Andric             // not (source file missing)
6900480093f4SDimitry Andric             if (m_selected_line >= static_cast<size_t>(m_first_visible_line)) {
6901480093f4SDimitry Andric               if (m_selected_line >= m_first_visible_line + num_visible_lines)
6902480093f4SDimitry Andric                 m_first_visible_line = m_selected_line - 10;
6903480093f4SDimitry Andric             } else {
6904480093f4SDimitry Andric               if (m_selected_line > 10)
6905480093f4SDimitry Andric                 m_first_visible_line = m_selected_line - 10;
6906480093f4SDimitry Andric               else
6907480093f4SDimitry Andric                 m_first_visible_line = 0;
6908480093f4SDimitry Andric             }
6909480093f4SDimitry Andric           } else {
6910480093f4SDimitry Andric             // File changed, set selected line to the line with the PC
6911480093f4SDimitry Andric             m_selected_line = m_pc_line;
6912480093f4SDimitry Andric             m_file_sp =
6913480093f4SDimitry Andric                 m_debugger.GetSourceManager().GetFile(m_sc.line_entry.file);
6914480093f4SDimitry Andric             if (m_file_sp) {
6915480093f4SDimitry Andric               const size_t num_lines = m_file_sp->GetNumLines();
6916480093f4SDimitry Andric               m_line_width = 1;
6917480093f4SDimitry Andric               for (size_t n = num_lines; n >= 10; n = n / 10)
6918480093f4SDimitry Andric                 ++m_line_width;
6919480093f4SDimitry Andric 
6920480093f4SDimitry Andric               if (num_lines < num_visible_lines ||
6921480093f4SDimitry Andric                   m_selected_line < num_visible_lines)
6922480093f4SDimitry Andric                 m_first_visible_line = 0;
6923480093f4SDimitry Andric               else
6924480093f4SDimitry Andric                 m_first_visible_line = m_selected_line - 10;
6925480093f4SDimitry Andric             }
6926480093f4SDimitry Andric           }
6927480093f4SDimitry Andric         } else {
6928480093f4SDimitry Andric           m_file_sp.reset();
6929480093f4SDimitry Andric         }
6930480093f4SDimitry Andric 
6931480093f4SDimitry Andric         if (!m_file_sp || m_file_sp->GetNumLines() == 0) {
6932480093f4SDimitry Andric           // Show disassembly
6933480093f4SDimitry Andric           bool prefer_file_cache = false;
6934480093f4SDimitry Andric           if (m_sc.function) {
6935480093f4SDimitry Andric             if (m_disassembly_scope != m_sc.function) {
6936480093f4SDimitry Andric               m_disassembly_scope = m_sc.function;
6937480093f4SDimitry Andric               m_disassembly_sp = m_sc.function->GetInstructions(
6938fe6060f1SDimitry Andric                   exe_ctx, nullptr, !prefer_file_cache);
6939480093f4SDimitry Andric               if (m_disassembly_sp) {
6940480093f4SDimitry Andric                 set_selected_line_to_pc = true;
6941480093f4SDimitry Andric                 m_disassembly_range = m_sc.function->GetAddressRange();
6942480093f4SDimitry Andric               } else {
6943480093f4SDimitry Andric                 m_disassembly_range.Clear();
6944480093f4SDimitry Andric               }
6945480093f4SDimitry Andric             } else {
6946480093f4SDimitry Andric               set_selected_line_to_pc = context_changed;
6947480093f4SDimitry Andric             }
6948480093f4SDimitry Andric           } else if (m_sc.symbol) {
6949480093f4SDimitry Andric             if (m_disassembly_scope != m_sc.symbol) {
6950480093f4SDimitry Andric               m_disassembly_scope = m_sc.symbol;
6951480093f4SDimitry Andric               m_disassembly_sp = m_sc.symbol->GetInstructions(
6952480093f4SDimitry Andric                   exe_ctx, nullptr, prefer_file_cache);
6953480093f4SDimitry Andric               if (m_disassembly_sp) {
6954480093f4SDimitry Andric                 set_selected_line_to_pc = true;
6955480093f4SDimitry Andric                 m_disassembly_range.GetBaseAddress() =
6956480093f4SDimitry Andric                     m_sc.symbol->GetAddress();
6957480093f4SDimitry Andric                 m_disassembly_range.SetByteSize(m_sc.symbol->GetByteSize());
6958480093f4SDimitry Andric               } else {
6959480093f4SDimitry Andric                 m_disassembly_range.Clear();
6960480093f4SDimitry Andric               }
6961480093f4SDimitry Andric             } else {
6962480093f4SDimitry Andric               set_selected_line_to_pc = context_changed;
6963480093f4SDimitry Andric             }
6964480093f4SDimitry Andric           }
6965480093f4SDimitry Andric         }
6966480093f4SDimitry Andric       } else {
6967480093f4SDimitry Andric         m_pc_line = UINT32_MAX;
6968480093f4SDimitry Andric       }
6969480093f4SDimitry Andric     }
6970480093f4SDimitry Andric 
6971480093f4SDimitry Andric     const int window_width = window.GetWidth();
6972480093f4SDimitry Andric     window.Erase();
6973480093f4SDimitry Andric     window.DrawTitleBox("Sources");
6974480093f4SDimitry Andric     if (!m_title.GetString().empty()) {
6975480093f4SDimitry Andric       window.AttributeOn(A_REVERSE);
6976480093f4SDimitry Andric       window.MoveCursor(1, 1);
6977480093f4SDimitry Andric       window.PutChar(' ');
6978e8d8bef9SDimitry Andric       window.PutCStringTruncated(1, m_title.GetString().str().c_str());
6979480093f4SDimitry Andric       int x = window.GetCursorX();
6980480093f4SDimitry Andric       if (x < window_width - 1) {
6981480093f4SDimitry Andric         window.Printf("%*s", window_width - x - 1, "");
6982480093f4SDimitry Andric       }
6983480093f4SDimitry Andric       window.AttributeOff(A_REVERSE);
6984480093f4SDimitry Andric     }
6985480093f4SDimitry Andric 
6986480093f4SDimitry Andric     Target *target = exe_ctx.GetTargetPtr();
6987480093f4SDimitry Andric     const size_t num_source_lines = GetNumSourceLines();
6988480093f4SDimitry Andric     if (num_source_lines > 0) {
6989480093f4SDimitry Andric       // Display source
6990480093f4SDimitry Andric       BreakpointLines bp_lines;
6991480093f4SDimitry Andric       if (target) {
6992480093f4SDimitry Andric         BreakpointList &bp_list = target->GetBreakpointList();
6993480093f4SDimitry Andric         const size_t num_bps = bp_list.GetSize();
6994480093f4SDimitry Andric         for (size_t bp_idx = 0; bp_idx < num_bps; ++bp_idx) {
6995480093f4SDimitry Andric           BreakpointSP bp_sp = bp_list.GetBreakpointAtIndex(bp_idx);
6996480093f4SDimitry Andric           const size_t num_bps_locs = bp_sp->GetNumLocations();
6997480093f4SDimitry Andric           for (size_t bp_loc_idx = 0; bp_loc_idx < num_bps_locs; ++bp_loc_idx) {
6998480093f4SDimitry Andric             BreakpointLocationSP bp_loc_sp =
6999480093f4SDimitry Andric                 bp_sp->GetLocationAtIndex(bp_loc_idx);
7000480093f4SDimitry Andric             LineEntry bp_loc_line_entry;
7001480093f4SDimitry Andric             if (bp_loc_sp->GetAddress().CalculateSymbolContextLineEntry(
7002480093f4SDimitry Andric                     bp_loc_line_entry)) {
7003480093f4SDimitry Andric               if (m_file_sp->GetFileSpec() == bp_loc_line_entry.file) {
7004480093f4SDimitry Andric                 bp_lines.insert(bp_loc_line_entry.line);
7005480093f4SDimitry Andric               }
7006480093f4SDimitry Andric             }
7007480093f4SDimitry Andric           }
7008480093f4SDimitry Andric         }
7009480093f4SDimitry Andric       }
7010480093f4SDimitry Andric 
7011480093f4SDimitry Andric       for (size_t i = 0; i < num_visible_lines; ++i) {
7012480093f4SDimitry Andric         const uint32_t curr_line = m_first_visible_line + i;
7013480093f4SDimitry Andric         if (curr_line < num_source_lines) {
7014480093f4SDimitry Andric           const int line_y = m_min_y + i;
7015480093f4SDimitry Andric           window.MoveCursor(1, line_y);
7016480093f4SDimitry Andric           const bool is_pc_line = curr_line == m_pc_line;
7017480093f4SDimitry Andric           const bool line_is_selected = m_selected_line == curr_line;
701881ad6265SDimitry Andric           // Highlight the line as the PC line first (done by passing
701981ad6265SDimitry Andric           // argument to OutputColoredStringTruncated()), then if the selected
702081ad6265SDimitry Andric           // line isn't the same as the PC line, highlight it differently.
7021480093f4SDimitry Andric           attr_t highlight_attr = 0;
7022480093f4SDimitry Andric           attr_t bp_attr = 0;
702381ad6265SDimitry Andric           if (line_is_selected && !is_pc_line)
702481ad6265SDimitry Andric             highlight_attr = A_REVERSE;
7025480093f4SDimitry Andric 
7026480093f4SDimitry Andric           if (bp_lines.find(curr_line + 1) != bp_lines.end())
7027e8d8bef9SDimitry Andric             bp_attr = COLOR_PAIR(BlackOnWhite);
7028480093f4SDimitry Andric 
7029480093f4SDimitry Andric           if (bp_attr)
7030480093f4SDimitry Andric             window.AttributeOn(bp_attr);
7031480093f4SDimitry Andric 
7032480093f4SDimitry Andric           window.Printf(" %*u ", m_line_width, curr_line + 1);
7033480093f4SDimitry Andric 
7034480093f4SDimitry Andric           if (bp_attr)
7035480093f4SDimitry Andric             window.AttributeOff(bp_attr);
7036480093f4SDimitry Andric 
7037480093f4SDimitry Andric           window.PutChar(ACS_VLINE);
7038480093f4SDimitry Andric           // Mark the line with the PC with a diamond
7039480093f4SDimitry Andric           if (is_pc_line)
7040480093f4SDimitry Andric             window.PutChar(ACS_DIAMOND);
7041480093f4SDimitry Andric           else
7042480093f4SDimitry Andric             window.PutChar(' ');
7043480093f4SDimitry Andric 
7044480093f4SDimitry Andric           if (highlight_attr)
7045480093f4SDimitry Andric             window.AttributeOn(highlight_attr);
7046e8d8bef9SDimitry Andric 
7047e8d8bef9SDimitry Andric           StreamString lineStream;
704881ad6265SDimitry Andric 
7049bdd1243dSDimitry Andric           std::optional<size_t> column;
705081ad6265SDimitry Andric           if (is_pc_line && m_sc.line_entry.IsValid() && m_sc.line_entry.column)
705181ad6265SDimitry Andric             column = m_sc.line_entry.column - 1;
705281ad6265SDimitry Andric           m_file_sp->DisplaySourceLines(curr_line + 1, column, 0, 0,
705381ad6265SDimitry Andric                                         &lineStream);
7054e8d8bef9SDimitry Andric           StringRef line = lineStream.GetString();
7055*5f757f3fSDimitry Andric           if (line.ends_with("\n"))
7056e8d8bef9SDimitry Andric             line = line.drop_back();
7057e8d8bef9SDimitry Andric           bool wasWritten = window.OutputColoredStringTruncated(
705881ad6265SDimitry Andric               1, line, m_first_visible_column, is_pc_line);
705981ad6265SDimitry Andric           if (!wasWritten && (line_is_selected || is_pc_line)) {
706081ad6265SDimitry Andric             // Draw an empty space to show the selected/PC line if empty,
7061e8d8bef9SDimitry Andric             // or draw '<' if nothing is visible because of scrolling too much
7062e8d8bef9SDimitry Andric             // to the right.
7063e8d8bef9SDimitry Andric             window.PutCStringTruncated(
7064e8d8bef9SDimitry Andric                 1, line.empty() && m_first_visible_column == 0 ? " " : "<");
7065e8d8bef9SDimitry Andric           }
7066480093f4SDimitry Andric 
7067480093f4SDimitry Andric           if (is_pc_line && frame_sp &&
7068480093f4SDimitry Andric               frame_sp->GetConcreteFrameIndex() == 0) {
7069480093f4SDimitry Andric             StopInfoSP stop_info_sp;
7070480093f4SDimitry Andric             if (thread)
7071480093f4SDimitry Andric               stop_info_sp = thread->GetStopInfo();
7072480093f4SDimitry Andric             if (stop_info_sp) {
7073480093f4SDimitry Andric               const char *stop_description = stop_info_sp->GetDescription();
7074480093f4SDimitry Andric               if (stop_description && stop_description[0]) {
7075480093f4SDimitry Andric                 size_t stop_description_len = strlen(stop_description);
7076480093f4SDimitry Andric                 int desc_x = window_width - stop_description_len - 16;
7077e8d8bef9SDimitry Andric                 if (desc_x - window.GetCursorX() > 0)
7078480093f4SDimitry Andric                   window.Printf("%*s", desc_x - window.GetCursorX(), "");
7079e8d8bef9SDimitry Andric                 window.MoveCursor(window_width - stop_description_len - 16,
7080e8d8bef9SDimitry Andric                                   line_y);
7081e8d8bef9SDimitry Andric                 const attr_t stop_reason_attr = COLOR_PAIR(WhiteOnBlue);
7082e8d8bef9SDimitry Andric                 window.AttributeOn(stop_reason_attr);
7083e8d8bef9SDimitry Andric                 window.PrintfTruncated(1, " <<< Thread %u: %s ",
7084e8d8bef9SDimitry Andric                                        thread->GetIndexID(), stop_description);
7085e8d8bef9SDimitry Andric                 window.AttributeOff(stop_reason_attr);
7086480093f4SDimitry Andric               }
7087480093f4SDimitry Andric             } else {
7088480093f4SDimitry Andric               window.Printf("%*s", window_width - window.GetCursorX() - 1, "");
7089480093f4SDimitry Andric             }
7090480093f4SDimitry Andric           }
7091480093f4SDimitry Andric           if (highlight_attr)
7092480093f4SDimitry Andric             window.AttributeOff(highlight_attr);
7093480093f4SDimitry Andric         } else {
7094480093f4SDimitry Andric           break;
7095480093f4SDimitry Andric         }
7096480093f4SDimitry Andric       }
7097480093f4SDimitry Andric     } else {
7098480093f4SDimitry Andric       size_t num_disassembly_lines = GetNumDisassemblyLines();
7099480093f4SDimitry Andric       if (num_disassembly_lines > 0) {
7100480093f4SDimitry Andric         // Display disassembly
7101480093f4SDimitry Andric         BreakpointAddrs bp_file_addrs;
7102480093f4SDimitry Andric         Target *target = exe_ctx.GetTargetPtr();
7103480093f4SDimitry Andric         if (target) {
7104480093f4SDimitry Andric           BreakpointList &bp_list = target->GetBreakpointList();
7105480093f4SDimitry Andric           const size_t num_bps = bp_list.GetSize();
7106480093f4SDimitry Andric           for (size_t bp_idx = 0; bp_idx < num_bps; ++bp_idx) {
7107480093f4SDimitry Andric             BreakpointSP bp_sp = bp_list.GetBreakpointAtIndex(bp_idx);
7108480093f4SDimitry Andric             const size_t num_bps_locs = bp_sp->GetNumLocations();
7109480093f4SDimitry Andric             for (size_t bp_loc_idx = 0; bp_loc_idx < num_bps_locs;
7110480093f4SDimitry Andric                  ++bp_loc_idx) {
7111480093f4SDimitry Andric               BreakpointLocationSP bp_loc_sp =
7112480093f4SDimitry Andric                   bp_sp->GetLocationAtIndex(bp_loc_idx);
7113480093f4SDimitry Andric               LineEntry bp_loc_line_entry;
7114480093f4SDimitry Andric               const lldb::addr_t file_addr =
7115480093f4SDimitry Andric                   bp_loc_sp->GetAddress().GetFileAddress();
7116480093f4SDimitry Andric               if (file_addr != LLDB_INVALID_ADDRESS) {
7117480093f4SDimitry Andric                 if (m_disassembly_range.ContainsFileAddress(file_addr))
7118480093f4SDimitry Andric                   bp_file_addrs.insert(file_addr);
7119480093f4SDimitry Andric               }
7120480093f4SDimitry Andric             }
7121480093f4SDimitry Andric           }
7122480093f4SDimitry Andric         }
7123480093f4SDimitry Andric 
7124480093f4SDimitry Andric         const attr_t selected_highlight_attr = A_REVERSE;
7125e8d8bef9SDimitry Andric         const attr_t pc_highlight_attr = COLOR_PAIR(WhiteOnBlue);
7126480093f4SDimitry Andric 
7127480093f4SDimitry Andric         StreamString strm;
7128480093f4SDimitry Andric 
7129480093f4SDimitry Andric         InstructionList &insts = m_disassembly_sp->GetInstructionList();
7130480093f4SDimitry Andric         Address pc_address;
7131480093f4SDimitry Andric 
7132480093f4SDimitry Andric         if (frame_sp)
7133480093f4SDimitry Andric           pc_address = frame_sp->GetFrameCodeAddress();
7134480093f4SDimitry Andric         const uint32_t pc_idx =
7135480093f4SDimitry Andric             pc_address.IsValid()
7136480093f4SDimitry Andric                 ? insts.GetIndexOfInstructionAtAddress(pc_address)
7137480093f4SDimitry Andric                 : UINT32_MAX;
7138480093f4SDimitry Andric         if (set_selected_line_to_pc) {
7139480093f4SDimitry Andric           m_selected_line = pc_idx;
7140480093f4SDimitry Andric         }
7141480093f4SDimitry Andric 
7142480093f4SDimitry Andric         const uint32_t non_visible_pc_offset = (num_visible_lines / 5);
7143480093f4SDimitry Andric         if (static_cast<size_t>(m_first_visible_line) >= num_disassembly_lines)
7144480093f4SDimitry Andric           m_first_visible_line = 0;
7145480093f4SDimitry Andric 
7146480093f4SDimitry Andric         if (pc_idx < num_disassembly_lines) {
7147480093f4SDimitry Andric           if (pc_idx < static_cast<uint32_t>(m_first_visible_line) ||
7148480093f4SDimitry Andric               pc_idx >= m_first_visible_line + num_visible_lines)
7149480093f4SDimitry Andric             m_first_visible_line = pc_idx - non_visible_pc_offset;
7150480093f4SDimitry Andric         }
7151480093f4SDimitry Andric 
7152480093f4SDimitry Andric         for (size_t i = 0; i < num_visible_lines; ++i) {
7153480093f4SDimitry Andric           const uint32_t inst_idx = m_first_visible_line + i;
7154480093f4SDimitry Andric           Instruction *inst = insts.GetInstructionAtIndex(inst_idx).get();
7155480093f4SDimitry Andric           if (!inst)
7156480093f4SDimitry Andric             break;
7157480093f4SDimitry Andric 
7158480093f4SDimitry Andric           const int line_y = m_min_y + i;
7159480093f4SDimitry Andric           window.MoveCursor(1, line_y);
7160480093f4SDimitry Andric           const bool is_pc_line = frame_sp && inst_idx == pc_idx;
7161480093f4SDimitry Andric           const bool line_is_selected = m_selected_line == inst_idx;
7162349cc55cSDimitry Andric           // Highlight the line as the PC line first, then if the selected
7163349cc55cSDimitry Andric           // line isn't the same as the PC line, highlight it differently
7164480093f4SDimitry Andric           attr_t highlight_attr = 0;
7165480093f4SDimitry Andric           attr_t bp_attr = 0;
7166480093f4SDimitry Andric           if (is_pc_line)
7167480093f4SDimitry Andric             highlight_attr = pc_highlight_attr;
7168480093f4SDimitry Andric           else if (line_is_selected)
7169480093f4SDimitry Andric             highlight_attr = selected_highlight_attr;
7170480093f4SDimitry Andric 
7171480093f4SDimitry Andric           if (bp_file_addrs.find(inst->GetAddress().GetFileAddress()) !=
7172480093f4SDimitry Andric               bp_file_addrs.end())
7173e8d8bef9SDimitry Andric             bp_attr = COLOR_PAIR(BlackOnWhite);
7174480093f4SDimitry Andric 
7175480093f4SDimitry Andric           if (bp_attr)
7176480093f4SDimitry Andric             window.AttributeOn(bp_attr);
7177480093f4SDimitry Andric 
7178480093f4SDimitry Andric           window.Printf(" 0x%16.16llx ",
7179480093f4SDimitry Andric                         static_cast<unsigned long long>(
7180480093f4SDimitry Andric                             inst->GetAddress().GetLoadAddress(target)));
7181480093f4SDimitry Andric 
7182480093f4SDimitry Andric           if (bp_attr)
7183480093f4SDimitry Andric             window.AttributeOff(bp_attr);
7184480093f4SDimitry Andric 
7185480093f4SDimitry Andric           window.PutChar(ACS_VLINE);
7186480093f4SDimitry Andric           // Mark the line with the PC with a diamond
7187480093f4SDimitry Andric           if (is_pc_line)
7188480093f4SDimitry Andric             window.PutChar(ACS_DIAMOND);
7189480093f4SDimitry Andric           else
7190480093f4SDimitry Andric             window.PutChar(' ');
7191480093f4SDimitry Andric 
7192480093f4SDimitry Andric           if (highlight_attr)
7193480093f4SDimitry Andric             window.AttributeOn(highlight_attr);
7194480093f4SDimitry Andric 
7195480093f4SDimitry Andric           const char *mnemonic = inst->GetMnemonic(&exe_ctx);
7196480093f4SDimitry Andric           const char *operands = inst->GetOperands(&exe_ctx);
7197480093f4SDimitry Andric           const char *comment = inst->GetComment(&exe_ctx);
7198480093f4SDimitry Andric 
7199480093f4SDimitry Andric           if (mnemonic != nullptr && mnemonic[0] == '\0')
7200480093f4SDimitry Andric             mnemonic = nullptr;
7201480093f4SDimitry Andric           if (operands != nullptr && operands[0] == '\0')
7202480093f4SDimitry Andric             operands = nullptr;
7203480093f4SDimitry Andric           if (comment != nullptr && comment[0] == '\0')
7204480093f4SDimitry Andric             comment = nullptr;
7205480093f4SDimitry Andric 
7206480093f4SDimitry Andric           strm.Clear();
7207480093f4SDimitry Andric 
7208480093f4SDimitry Andric           if (mnemonic != nullptr && operands != nullptr && comment != nullptr)
7209480093f4SDimitry Andric             strm.Printf("%-8s %-25s ; %s", mnemonic, operands, comment);
7210480093f4SDimitry Andric           else if (mnemonic != nullptr && operands != nullptr)
7211480093f4SDimitry Andric             strm.Printf("%-8s %s", mnemonic, operands);
7212480093f4SDimitry Andric           else if (mnemonic != nullptr)
7213480093f4SDimitry Andric             strm.Printf("%s", mnemonic);
7214480093f4SDimitry Andric 
7215480093f4SDimitry Andric           int right_pad = 1;
7216e8d8bef9SDimitry Andric           window.PutCStringTruncated(
7217e8d8bef9SDimitry Andric               right_pad,
7218e8d8bef9SDimitry Andric               strm.GetString().substr(m_first_visible_column).data());
7219480093f4SDimitry Andric 
7220480093f4SDimitry Andric           if (is_pc_line && frame_sp &&
7221480093f4SDimitry Andric               frame_sp->GetConcreteFrameIndex() == 0) {
7222480093f4SDimitry Andric             StopInfoSP stop_info_sp;
7223480093f4SDimitry Andric             if (thread)
7224480093f4SDimitry Andric               stop_info_sp = thread->GetStopInfo();
7225480093f4SDimitry Andric             if (stop_info_sp) {
7226480093f4SDimitry Andric               const char *stop_description = stop_info_sp->GetDescription();
7227480093f4SDimitry Andric               if (stop_description && stop_description[0]) {
7228480093f4SDimitry Andric                 size_t stop_description_len = strlen(stop_description);
7229480093f4SDimitry Andric                 int desc_x = window_width - stop_description_len - 16;
7230e8d8bef9SDimitry Andric                 if (desc_x - window.GetCursorX() > 0)
7231480093f4SDimitry Andric                   window.Printf("%*s", desc_x - window.GetCursorX(), "");
7232e8d8bef9SDimitry Andric                 window.MoveCursor(window_width - stop_description_len - 15,
7233e8d8bef9SDimitry Andric                                   line_y);
7234bdd1243dSDimitry Andric                 if (thread)
7235e8d8bef9SDimitry Andric                   window.PrintfTruncated(1, "<<< Thread %u: %s ",
7236bdd1243dSDimitry Andric                                          thread->GetIndexID(),
7237bdd1243dSDimitry Andric                                          stop_description);
7238480093f4SDimitry Andric               }
7239480093f4SDimitry Andric             } else {
7240480093f4SDimitry Andric               window.Printf("%*s", window_width - window.GetCursorX() - 1, "");
7241480093f4SDimitry Andric             }
7242480093f4SDimitry Andric           }
7243480093f4SDimitry Andric           if (highlight_attr)
7244480093f4SDimitry Andric             window.AttributeOff(highlight_attr);
7245480093f4SDimitry Andric         }
7246480093f4SDimitry Andric       }
7247480093f4SDimitry Andric     }
7248480093f4SDimitry Andric     return true; // Drawing handled
7249480093f4SDimitry Andric   }
7250480093f4SDimitry Andric 
GetNumLines()7251480093f4SDimitry Andric   size_t GetNumLines() {
7252480093f4SDimitry Andric     size_t num_lines = GetNumSourceLines();
7253480093f4SDimitry Andric     if (num_lines == 0)
7254480093f4SDimitry Andric       num_lines = GetNumDisassemblyLines();
7255480093f4SDimitry Andric     return num_lines;
7256480093f4SDimitry Andric   }
7257480093f4SDimitry Andric 
GetNumSourceLines() const7258480093f4SDimitry Andric   size_t GetNumSourceLines() const {
7259480093f4SDimitry Andric     if (m_file_sp)
7260480093f4SDimitry Andric       return m_file_sp->GetNumLines();
7261480093f4SDimitry Andric     return 0;
7262480093f4SDimitry Andric   }
7263480093f4SDimitry Andric 
GetNumDisassemblyLines() const7264480093f4SDimitry Andric   size_t GetNumDisassemblyLines() const {
7265480093f4SDimitry Andric     if (m_disassembly_sp)
7266480093f4SDimitry Andric       return m_disassembly_sp->GetInstructionList().GetSize();
7267480093f4SDimitry Andric     return 0;
7268480093f4SDimitry Andric   }
7269480093f4SDimitry Andric 
WindowDelegateHandleChar(Window & window,int c)7270480093f4SDimitry Andric   HandleCharResult WindowDelegateHandleChar(Window &window, int c) override {
7271480093f4SDimitry Andric     const uint32_t num_visible_lines = NumVisibleLines();
7272480093f4SDimitry Andric     const size_t num_lines = GetNumLines();
7273480093f4SDimitry Andric 
7274480093f4SDimitry Andric     switch (c) {
7275480093f4SDimitry Andric     case ',':
7276480093f4SDimitry Andric     case KEY_PPAGE:
7277480093f4SDimitry Andric       // Page up key
7278480093f4SDimitry Andric       if (static_cast<uint32_t>(m_first_visible_line) > num_visible_lines)
7279480093f4SDimitry Andric         m_first_visible_line -= num_visible_lines;
7280480093f4SDimitry Andric       else
7281480093f4SDimitry Andric         m_first_visible_line = 0;
7282480093f4SDimitry Andric       m_selected_line = m_first_visible_line;
7283480093f4SDimitry Andric       return eKeyHandled;
7284480093f4SDimitry Andric 
7285480093f4SDimitry Andric     case '.':
7286480093f4SDimitry Andric     case KEY_NPAGE:
7287480093f4SDimitry Andric       // Page down key
7288480093f4SDimitry Andric       {
7289480093f4SDimitry Andric         if (m_first_visible_line + num_visible_lines < num_lines)
7290480093f4SDimitry Andric           m_first_visible_line += num_visible_lines;
7291480093f4SDimitry Andric         else if (num_lines < num_visible_lines)
7292480093f4SDimitry Andric           m_first_visible_line = 0;
7293480093f4SDimitry Andric         else
7294480093f4SDimitry Andric           m_first_visible_line = num_lines - num_visible_lines;
7295480093f4SDimitry Andric         m_selected_line = m_first_visible_line;
7296480093f4SDimitry Andric       }
7297480093f4SDimitry Andric       return eKeyHandled;
7298480093f4SDimitry Andric 
7299480093f4SDimitry Andric     case KEY_UP:
7300480093f4SDimitry Andric       if (m_selected_line > 0) {
7301480093f4SDimitry Andric         m_selected_line--;
7302480093f4SDimitry Andric         if (static_cast<size_t>(m_first_visible_line) > m_selected_line)
7303480093f4SDimitry Andric           m_first_visible_line = m_selected_line;
7304480093f4SDimitry Andric       }
7305480093f4SDimitry Andric       return eKeyHandled;
7306480093f4SDimitry Andric 
7307480093f4SDimitry Andric     case KEY_DOWN:
7308480093f4SDimitry Andric       if (m_selected_line + 1 < num_lines) {
7309480093f4SDimitry Andric         m_selected_line++;
7310480093f4SDimitry Andric         if (m_first_visible_line + num_visible_lines < m_selected_line)
7311480093f4SDimitry Andric           m_first_visible_line++;
7312480093f4SDimitry Andric       }
7313480093f4SDimitry Andric       return eKeyHandled;
7314480093f4SDimitry Andric 
7315e8d8bef9SDimitry Andric     case KEY_LEFT:
7316e8d8bef9SDimitry Andric       if (m_first_visible_column > 0)
7317e8d8bef9SDimitry Andric         --m_first_visible_column;
7318e8d8bef9SDimitry Andric       return eKeyHandled;
7319e8d8bef9SDimitry Andric 
7320e8d8bef9SDimitry Andric     case KEY_RIGHT:
7321e8d8bef9SDimitry Andric       ++m_first_visible_column;
7322e8d8bef9SDimitry Andric       return eKeyHandled;
7323e8d8bef9SDimitry Andric 
7324480093f4SDimitry Andric     case '\r':
7325480093f4SDimitry Andric     case '\n':
7326480093f4SDimitry Andric     case KEY_ENTER:
7327480093f4SDimitry Andric       // Set a breakpoint and run to the line using a one shot breakpoint
7328480093f4SDimitry Andric       if (GetNumSourceLines() > 0) {
7329480093f4SDimitry Andric         ExecutionContext exe_ctx =
7330480093f4SDimitry Andric             m_debugger.GetCommandInterpreter().GetExecutionContext();
7331480093f4SDimitry Andric         if (exe_ctx.HasProcessScope() && exe_ctx.GetProcessRef().IsAlive()) {
7332480093f4SDimitry Andric           BreakpointSP bp_sp = exe_ctx.GetTargetRef().CreateBreakpoint(
7333480093f4SDimitry Andric               nullptr, // Don't limit the breakpoint to certain modules
7334480093f4SDimitry Andric               m_file_sp->GetFileSpec(), // Source file
7335480093f4SDimitry Andric               m_selected_line +
7336480093f4SDimitry Andric                   1, // Source line number (m_selected_line is zero based)
7337480093f4SDimitry Andric               0,     // Unspecified column.
7338480093f4SDimitry Andric               0,     // No offset
7339480093f4SDimitry Andric               eLazyBoolCalculate,  // Check inlines using global setting
7340480093f4SDimitry Andric               eLazyBoolCalculate,  // Skip prologue using global setting,
7341480093f4SDimitry Andric               false,               // internal
7342480093f4SDimitry Andric               false,               // request_hardware
7343480093f4SDimitry Andric               eLazyBoolCalculate); // move_to_nearest_code
7344480093f4SDimitry Andric           // Make breakpoint one shot
7345fe6060f1SDimitry Andric           bp_sp->GetOptions().SetOneShot(true);
7346480093f4SDimitry Andric           exe_ctx.GetProcessRef().Resume();
7347480093f4SDimitry Andric         }
7348480093f4SDimitry Andric       } else if (m_selected_line < GetNumDisassemblyLines()) {
7349480093f4SDimitry Andric         const Instruction *inst = m_disassembly_sp->GetInstructionList()
7350480093f4SDimitry Andric                                       .GetInstructionAtIndex(m_selected_line)
7351480093f4SDimitry Andric                                       .get();
7352480093f4SDimitry Andric         ExecutionContext exe_ctx =
7353480093f4SDimitry Andric             m_debugger.GetCommandInterpreter().GetExecutionContext();
7354480093f4SDimitry Andric         if (exe_ctx.HasTargetScope()) {
7355480093f4SDimitry Andric           Address addr = inst->GetAddress();
7356480093f4SDimitry Andric           BreakpointSP bp_sp = exe_ctx.GetTargetRef().CreateBreakpoint(
7357480093f4SDimitry Andric               addr,   // lldb_private::Address
7358480093f4SDimitry Andric               false,  // internal
7359480093f4SDimitry Andric               false); // request_hardware
7360480093f4SDimitry Andric           // Make breakpoint one shot
7361fe6060f1SDimitry Andric           bp_sp->GetOptions().SetOneShot(true);
7362480093f4SDimitry Andric           exe_ctx.GetProcessRef().Resume();
7363480093f4SDimitry Andric         }
7364480093f4SDimitry Andric       }
7365480093f4SDimitry Andric       return eKeyHandled;
7366480093f4SDimitry Andric 
7367480093f4SDimitry Andric     case 'b': // 'b' == toggle breakpoint on currently selected line
7368e8d8bef9SDimitry Andric       ToggleBreakpointOnSelectedLine();
7369480093f4SDimitry Andric       return eKeyHandled;
7370480093f4SDimitry Andric 
7371480093f4SDimitry Andric     case 'D': // 'D' == detach and keep stopped
7372480093f4SDimitry Andric     {
7373480093f4SDimitry Andric       ExecutionContext exe_ctx =
7374480093f4SDimitry Andric           m_debugger.GetCommandInterpreter().GetExecutionContext();
7375480093f4SDimitry Andric       if (exe_ctx.HasProcessScope())
7376e8d8bef9SDimitry Andric         exe_ctx.GetProcessRef().Detach(true);
7377480093f4SDimitry Andric     }
7378480093f4SDimitry Andric       return eKeyHandled;
7379480093f4SDimitry Andric 
7380480093f4SDimitry Andric     case 'c':
7381480093f4SDimitry Andric       // 'c' == continue
7382480093f4SDimitry Andric       {
7383480093f4SDimitry Andric         ExecutionContext exe_ctx =
7384480093f4SDimitry Andric             m_debugger.GetCommandInterpreter().GetExecutionContext();
7385480093f4SDimitry Andric         if (exe_ctx.HasProcessScope())
7386480093f4SDimitry Andric           exe_ctx.GetProcessRef().Resume();
7387480093f4SDimitry Andric       }
7388480093f4SDimitry Andric       return eKeyHandled;
7389480093f4SDimitry Andric 
7390e8d8bef9SDimitry Andric     case 'f':
7391e8d8bef9SDimitry Andric       // 'f' == step out (finish)
7392480093f4SDimitry Andric       {
7393480093f4SDimitry Andric         ExecutionContext exe_ctx =
7394480093f4SDimitry Andric             m_debugger.GetCommandInterpreter().GetExecutionContext();
7395480093f4SDimitry Andric         if (exe_ctx.HasThreadScope() &&
7396480093f4SDimitry Andric             StateIsStoppedState(exe_ctx.GetProcessRef().GetState(), true)) {
739781ad6265SDimitry Andric           Thread *thread = exe_ctx.GetThreadPtr();
739806c3fb27SDimitry Andric           uint32_t frame_idx =
739906c3fb27SDimitry Andric               thread->GetSelectedFrameIndex(SelectMostRelevantFrame);
740081ad6265SDimitry Andric           exe_ctx.GetThreadRef().StepOut(frame_idx);
7401480093f4SDimitry Andric         }
7402480093f4SDimitry Andric       }
7403480093f4SDimitry Andric       return eKeyHandled;
7404480093f4SDimitry Andric 
7405480093f4SDimitry Andric     case 'n': // 'n' == step over
7406480093f4SDimitry Andric     case 'N': // 'N' == step over instruction
7407480093f4SDimitry Andric     {
7408480093f4SDimitry Andric       ExecutionContext exe_ctx =
7409480093f4SDimitry Andric           m_debugger.GetCommandInterpreter().GetExecutionContext();
7410480093f4SDimitry Andric       if (exe_ctx.HasThreadScope() &&
7411480093f4SDimitry Andric           StateIsStoppedState(exe_ctx.GetProcessRef().GetState(), true)) {
7412480093f4SDimitry Andric         bool source_step = (c == 'n');
7413480093f4SDimitry Andric         exe_ctx.GetThreadRef().StepOver(source_step);
7414480093f4SDimitry Andric       }
7415480093f4SDimitry Andric     }
7416480093f4SDimitry Andric       return eKeyHandled;
7417480093f4SDimitry Andric 
7418480093f4SDimitry Andric     case 's': // 's' == step into
7419480093f4SDimitry Andric     case 'S': // 'S' == step into instruction
7420480093f4SDimitry Andric     {
7421480093f4SDimitry Andric       ExecutionContext exe_ctx =
7422480093f4SDimitry Andric           m_debugger.GetCommandInterpreter().GetExecutionContext();
7423480093f4SDimitry Andric       if (exe_ctx.HasThreadScope() &&
7424480093f4SDimitry Andric           StateIsStoppedState(exe_ctx.GetProcessRef().GetState(), true)) {
7425480093f4SDimitry Andric         bool source_step = (c == 's');
7426480093f4SDimitry Andric         exe_ctx.GetThreadRef().StepIn(source_step);
7427480093f4SDimitry Andric       }
7428480093f4SDimitry Andric     }
7429480093f4SDimitry Andric       return eKeyHandled;
7430480093f4SDimitry Andric 
7431e8d8bef9SDimitry Andric     case 'u': // 'u' == frame up
7432e8d8bef9SDimitry Andric     case 'd': // 'd' == frame down
7433e8d8bef9SDimitry Andric     {
7434e8d8bef9SDimitry Andric       ExecutionContext exe_ctx =
7435e8d8bef9SDimitry Andric           m_debugger.GetCommandInterpreter().GetExecutionContext();
7436e8d8bef9SDimitry Andric       if (exe_ctx.HasThreadScope()) {
7437e8d8bef9SDimitry Andric         Thread *thread = exe_ctx.GetThreadPtr();
743806c3fb27SDimitry Andric         uint32_t frame_idx =
743906c3fb27SDimitry Andric             thread->GetSelectedFrameIndex(SelectMostRelevantFrame);
7440e8d8bef9SDimitry Andric         if (frame_idx == UINT32_MAX)
7441e8d8bef9SDimitry Andric           frame_idx = 0;
7442e8d8bef9SDimitry Andric         if (c == 'u' && frame_idx + 1 < thread->GetStackFrameCount())
7443e8d8bef9SDimitry Andric           ++frame_idx;
7444e8d8bef9SDimitry Andric         else if (c == 'd' && frame_idx > 0)
7445e8d8bef9SDimitry Andric           --frame_idx;
7446e8d8bef9SDimitry Andric         if (thread->SetSelectedFrameByIndex(frame_idx, true))
744706c3fb27SDimitry Andric           exe_ctx.SetFrameSP(thread->GetSelectedFrame(SelectMostRelevantFrame));
7448e8d8bef9SDimitry Andric       }
7449e8d8bef9SDimitry Andric     }
7450e8d8bef9SDimitry Andric       return eKeyHandled;
7451e8d8bef9SDimitry Andric 
7452480093f4SDimitry Andric     case 'h':
7453480093f4SDimitry Andric       window.CreateHelpSubwindow();
7454480093f4SDimitry Andric       return eKeyHandled;
7455480093f4SDimitry Andric 
7456480093f4SDimitry Andric     default:
7457480093f4SDimitry Andric       break;
7458480093f4SDimitry Andric     }
7459480093f4SDimitry Andric     return eKeyNotHandled;
7460480093f4SDimitry Andric   }
7461480093f4SDimitry Andric 
ToggleBreakpointOnSelectedLine()7462e8d8bef9SDimitry Andric   void ToggleBreakpointOnSelectedLine() {
7463e8d8bef9SDimitry Andric     ExecutionContext exe_ctx =
7464e8d8bef9SDimitry Andric         m_debugger.GetCommandInterpreter().GetExecutionContext();
7465e8d8bef9SDimitry Andric     if (!exe_ctx.HasTargetScope())
7466e8d8bef9SDimitry Andric       return;
7467e8d8bef9SDimitry Andric     if (GetNumSourceLines() > 0) {
7468e8d8bef9SDimitry Andric       // Source file breakpoint.
7469e8d8bef9SDimitry Andric       BreakpointList &bp_list = exe_ctx.GetTargetRef().GetBreakpointList();
7470e8d8bef9SDimitry Andric       const size_t num_bps = bp_list.GetSize();
7471e8d8bef9SDimitry Andric       for (size_t bp_idx = 0; bp_idx < num_bps; ++bp_idx) {
7472e8d8bef9SDimitry Andric         BreakpointSP bp_sp = bp_list.GetBreakpointAtIndex(bp_idx);
7473e8d8bef9SDimitry Andric         const size_t num_bps_locs = bp_sp->GetNumLocations();
7474e8d8bef9SDimitry Andric         for (size_t bp_loc_idx = 0; bp_loc_idx < num_bps_locs; ++bp_loc_idx) {
7475e8d8bef9SDimitry Andric           BreakpointLocationSP bp_loc_sp =
7476e8d8bef9SDimitry Andric               bp_sp->GetLocationAtIndex(bp_loc_idx);
7477e8d8bef9SDimitry Andric           LineEntry bp_loc_line_entry;
7478e8d8bef9SDimitry Andric           if (bp_loc_sp->GetAddress().CalculateSymbolContextLineEntry(
7479e8d8bef9SDimitry Andric                   bp_loc_line_entry)) {
7480e8d8bef9SDimitry Andric             if (m_file_sp->GetFileSpec() == bp_loc_line_entry.file &&
7481e8d8bef9SDimitry Andric                 m_selected_line + 1 == bp_loc_line_entry.line) {
7482e8d8bef9SDimitry Andric               bool removed =
7483e8d8bef9SDimitry Andric                   exe_ctx.GetTargetRef().RemoveBreakpointByID(bp_sp->GetID());
7484e8d8bef9SDimitry Andric               assert(removed);
7485e8d8bef9SDimitry Andric               UNUSED_IF_ASSERT_DISABLED(removed);
7486e8d8bef9SDimitry Andric               return; // Existing breakpoint removed.
7487e8d8bef9SDimitry Andric             }
7488e8d8bef9SDimitry Andric           }
7489e8d8bef9SDimitry Andric         }
7490e8d8bef9SDimitry Andric       }
7491e8d8bef9SDimitry Andric       // No breakpoint found on the location, add it.
7492e8d8bef9SDimitry Andric       BreakpointSP bp_sp = exe_ctx.GetTargetRef().CreateBreakpoint(
7493e8d8bef9SDimitry Andric           nullptr, // Don't limit the breakpoint to certain modules
7494e8d8bef9SDimitry Andric           m_file_sp->GetFileSpec(), // Source file
7495e8d8bef9SDimitry Andric           m_selected_line +
7496e8d8bef9SDimitry Andric               1, // Source line number (m_selected_line is zero based)
7497e8d8bef9SDimitry Andric           0,     // No column specified.
7498e8d8bef9SDimitry Andric           0,     // No offset
7499e8d8bef9SDimitry Andric           eLazyBoolCalculate,  // Check inlines using global setting
7500e8d8bef9SDimitry Andric           eLazyBoolCalculate,  // Skip prologue using global setting,
7501e8d8bef9SDimitry Andric           false,               // internal
7502e8d8bef9SDimitry Andric           false,               // request_hardware
7503e8d8bef9SDimitry Andric           eLazyBoolCalculate); // move_to_nearest_code
7504e8d8bef9SDimitry Andric     } else {
7505e8d8bef9SDimitry Andric       // Disassembly breakpoint.
7506e8d8bef9SDimitry Andric       assert(GetNumDisassemblyLines() > 0);
7507e8d8bef9SDimitry Andric       assert(m_selected_line < GetNumDisassemblyLines());
7508e8d8bef9SDimitry Andric       const Instruction *inst = m_disassembly_sp->GetInstructionList()
7509e8d8bef9SDimitry Andric                                     .GetInstructionAtIndex(m_selected_line)
7510e8d8bef9SDimitry Andric                                     .get();
7511e8d8bef9SDimitry Andric       Address addr = inst->GetAddress();
7512e8d8bef9SDimitry Andric       // Try to find it.
7513e8d8bef9SDimitry Andric       BreakpointList &bp_list = exe_ctx.GetTargetRef().GetBreakpointList();
7514e8d8bef9SDimitry Andric       const size_t num_bps = bp_list.GetSize();
7515e8d8bef9SDimitry Andric       for (size_t bp_idx = 0; bp_idx < num_bps; ++bp_idx) {
7516e8d8bef9SDimitry Andric         BreakpointSP bp_sp = bp_list.GetBreakpointAtIndex(bp_idx);
7517e8d8bef9SDimitry Andric         const size_t num_bps_locs = bp_sp->GetNumLocations();
7518e8d8bef9SDimitry Andric         for (size_t bp_loc_idx = 0; bp_loc_idx < num_bps_locs; ++bp_loc_idx) {
7519e8d8bef9SDimitry Andric           BreakpointLocationSP bp_loc_sp =
7520e8d8bef9SDimitry Andric               bp_sp->GetLocationAtIndex(bp_loc_idx);
7521e8d8bef9SDimitry Andric           LineEntry bp_loc_line_entry;
7522e8d8bef9SDimitry Andric           const lldb::addr_t file_addr =
7523e8d8bef9SDimitry Andric               bp_loc_sp->GetAddress().GetFileAddress();
7524e8d8bef9SDimitry Andric           if (file_addr == addr.GetFileAddress()) {
7525e8d8bef9SDimitry Andric             bool removed =
7526e8d8bef9SDimitry Andric                 exe_ctx.GetTargetRef().RemoveBreakpointByID(bp_sp->GetID());
7527e8d8bef9SDimitry Andric             assert(removed);
7528e8d8bef9SDimitry Andric             UNUSED_IF_ASSERT_DISABLED(removed);
7529e8d8bef9SDimitry Andric             return; // Existing breakpoint removed.
7530e8d8bef9SDimitry Andric           }
7531e8d8bef9SDimitry Andric         }
7532e8d8bef9SDimitry Andric       }
7533e8d8bef9SDimitry Andric       // No breakpoint found on the address, add it.
7534e8d8bef9SDimitry Andric       BreakpointSP bp_sp =
7535e8d8bef9SDimitry Andric           exe_ctx.GetTargetRef().CreateBreakpoint(addr, // lldb_private::Address
7536e8d8bef9SDimitry Andric                                                   false,  // internal
7537e8d8bef9SDimitry Andric                                                   false); // request_hardware
7538e8d8bef9SDimitry Andric     }
7539e8d8bef9SDimitry Andric   }
7540e8d8bef9SDimitry Andric 
7541480093f4SDimitry Andric protected:
7542480093f4SDimitry Andric   typedef std::set<uint32_t> BreakpointLines;
7543480093f4SDimitry Andric   typedef std::set<lldb::addr_t> BreakpointAddrs;
7544480093f4SDimitry Andric 
7545480093f4SDimitry Andric   Debugger &m_debugger;
7546480093f4SDimitry Andric   SymbolContext m_sc;
7547480093f4SDimitry Andric   SourceManager::FileSP m_file_sp;
754881ad6265SDimitry Andric   SymbolContextScope *m_disassembly_scope = nullptr;
7549480093f4SDimitry Andric   lldb::DisassemblerSP m_disassembly_sp;
7550480093f4SDimitry Andric   AddressRange m_disassembly_range;
7551480093f4SDimitry Andric   StreamString m_title;
755281ad6265SDimitry Andric   lldb::user_id_t m_tid = LLDB_INVALID_THREAD_ID;
755381ad6265SDimitry Andric   int m_line_width = 4;
755481ad6265SDimitry Andric   uint32_t m_selected_line = 0; // The selected line
755581ad6265SDimitry Andric   uint32_t m_pc_line = 0;       // The line with the PC
755681ad6265SDimitry Andric   uint32_t m_stop_id = 0;
755781ad6265SDimitry Andric   uint32_t m_frame_idx = UINT32_MAX;
755881ad6265SDimitry Andric   int m_first_visible_line = 0;
755981ad6265SDimitry Andric   int m_first_visible_column = 0;
756081ad6265SDimitry Andric   int m_min_x = 0;
756181ad6265SDimitry Andric   int m_min_y = 0;
756281ad6265SDimitry Andric   int m_max_x = 0;
756381ad6265SDimitry Andric   int m_max_y = 0;
7564480093f4SDimitry Andric };
7565480093f4SDimitry Andric 
7566480093f4SDimitry Andric DisplayOptions ValueObjectListDelegate::g_options = {true};
7567480093f4SDimitry Andric 
IOHandlerCursesGUI(Debugger & debugger)7568480093f4SDimitry Andric IOHandlerCursesGUI::IOHandlerCursesGUI(Debugger &debugger)
7569480093f4SDimitry Andric     : IOHandler(debugger, IOHandler::Type::Curses) {}
7570480093f4SDimitry Andric 
Activate()7571480093f4SDimitry Andric void IOHandlerCursesGUI::Activate() {
7572480093f4SDimitry Andric   IOHandler::Activate();
7573480093f4SDimitry Andric   if (!m_app_ap) {
75745ffd83dbSDimitry Andric     m_app_ap = std::make_unique<Application>(GetInputFILE(), GetOutputFILE());
7575480093f4SDimitry Andric 
7576480093f4SDimitry Andric     // This is both a window and a menu delegate
7577480093f4SDimitry Andric     std::shared_ptr<ApplicationDelegate> app_delegate_sp(
7578480093f4SDimitry Andric         new ApplicationDelegate(*m_app_ap, m_debugger));
7579480093f4SDimitry Andric 
7580480093f4SDimitry Andric     MenuDelegateSP app_menu_delegate_sp =
7581480093f4SDimitry Andric         std::static_pointer_cast<MenuDelegate>(app_delegate_sp);
7582480093f4SDimitry Andric     MenuSP lldb_menu_sp(
7583480093f4SDimitry Andric         new Menu("LLDB", "F1", KEY_F(1), ApplicationDelegate::eMenuID_LLDB));
7584480093f4SDimitry Andric     MenuSP exit_menuitem_sp(
7585480093f4SDimitry Andric         new Menu("Exit", nullptr, 'x', ApplicationDelegate::eMenuID_LLDBExit));
7586480093f4SDimitry Andric     exit_menuitem_sp->SetCannedResult(MenuActionResult::Quit);
7587480093f4SDimitry Andric     lldb_menu_sp->AddSubmenu(MenuSP(new Menu(
7588480093f4SDimitry Andric         "About LLDB", nullptr, 'a', ApplicationDelegate::eMenuID_LLDBAbout)));
7589480093f4SDimitry Andric     lldb_menu_sp->AddSubmenu(MenuSP(new Menu(Menu::Type::Separator)));
7590480093f4SDimitry Andric     lldb_menu_sp->AddSubmenu(exit_menuitem_sp);
7591480093f4SDimitry Andric 
7592480093f4SDimitry Andric     MenuSP target_menu_sp(new Menu("Target", "F2", KEY_F(2),
7593480093f4SDimitry Andric                                    ApplicationDelegate::eMenuID_Target));
7594480093f4SDimitry Andric     target_menu_sp->AddSubmenu(MenuSP(new Menu(
7595480093f4SDimitry Andric         "Create", nullptr, 'c', ApplicationDelegate::eMenuID_TargetCreate)));
7596480093f4SDimitry Andric     target_menu_sp->AddSubmenu(MenuSP(new Menu(
7597480093f4SDimitry Andric         "Delete", nullptr, 'd', ApplicationDelegate::eMenuID_TargetDelete)));
7598480093f4SDimitry Andric 
7599480093f4SDimitry Andric     MenuSP process_menu_sp(new Menu("Process", "F3", KEY_F(3),
7600480093f4SDimitry Andric                                     ApplicationDelegate::eMenuID_Process));
7601480093f4SDimitry Andric     process_menu_sp->AddSubmenu(MenuSP(new Menu(
7602480093f4SDimitry Andric         "Attach", nullptr, 'a', ApplicationDelegate::eMenuID_ProcessAttach)));
7603e8d8bef9SDimitry Andric     process_menu_sp->AddSubmenu(
7604e8d8bef9SDimitry Andric         MenuSP(new Menu("Detach and resume", nullptr, 'd',
7605e8d8bef9SDimitry Andric                         ApplicationDelegate::eMenuID_ProcessDetachResume)));
7606e8d8bef9SDimitry Andric     process_menu_sp->AddSubmenu(
7607e8d8bef9SDimitry Andric         MenuSP(new Menu("Detach suspended", nullptr, 's',
7608e8d8bef9SDimitry Andric                         ApplicationDelegate::eMenuID_ProcessDetachSuspended)));
7609480093f4SDimitry Andric     process_menu_sp->AddSubmenu(MenuSP(new Menu(
7610480093f4SDimitry Andric         "Launch", nullptr, 'l', ApplicationDelegate::eMenuID_ProcessLaunch)));
7611480093f4SDimitry Andric     process_menu_sp->AddSubmenu(MenuSP(new Menu(Menu::Type::Separator)));
7612480093f4SDimitry Andric     process_menu_sp->AddSubmenu(
7613480093f4SDimitry Andric         MenuSP(new Menu("Continue", nullptr, 'c',
7614480093f4SDimitry Andric                         ApplicationDelegate::eMenuID_ProcessContinue)));
7615480093f4SDimitry Andric     process_menu_sp->AddSubmenu(MenuSP(new Menu(
7616480093f4SDimitry Andric         "Halt", nullptr, 'h', ApplicationDelegate::eMenuID_ProcessHalt)));
7617480093f4SDimitry Andric     process_menu_sp->AddSubmenu(MenuSP(new Menu(
7618480093f4SDimitry Andric         "Kill", nullptr, 'k', ApplicationDelegate::eMenuID_ProcessKill)));
7619480093f4SDimitry Andric 
7620480093f4SDimitry Andric     MenuSP thread_menu_sp(new Menu("Thread", "F4", KEY_F(4),
7621480093f4SDimitry Andric                                    ApplicationDelegate::eMenuID_Thread));
7622480093f4SDimitry Andric     thread_menu_sp->AddSubmenu(MenuSP(new Menu(
7623480093f4SDimitry Andric         "Step In", nullptr, 'i', ApplicationDelegate::eMenuID_ThreadStepIn)));
7624480093f4SDimitry Andric     thread_menu_sp->AddSubmenu(
7625480093f4SDimitry Andric         MenuSP(new Menu("Step Over", nullptr, 'v',
7626480093f4SDimitry Andric                         ApplicationDelegate::eMenuID_ThreadStepOver)));
7627480093f4SDimitry Andric     thread_menu_sp->AddSubmenu(MenuSP(new Menu(
7628480093f4SDimitry Andric         "Step Out", nullptr, 'o', ApplicationDelegate::eMenuID_ThreadStepOut)));
7629480093f4SDimitry Andric 
7630480093f4SDimitry Andric     MenuSP view_menu_sp(
7631480093f4SDimitry Andric         new Menu("View", "F5", KEY_F(5), ApplicationDelegate::eMenuID_View));
7632480093f4SDimitry Andric     view_menu_sp->AddSubmenu(
7633349cc55cSDimitry Andric         MenuSP(new Menu("Backtrace", nullptr, 't',
7634480093f4SDimitry Andric                         ApplicationDelegate::eMenuID_ViewBacktrace)));
7635480093f4SDimitry Andric     view_menu_sp->AddSubmenu(
7636480093f4SDimitry Andric         MenuSP(new Menu("Registers", nullptr, 'r',
7637480093f4SDimitry Andric                         ApplicationDelegate::eMenuID_ViewRegisters)));
7638480093f4SDimitry Andric     view_menu_sp->AddSubmenu(MenuSP(new Menu(
7639480093f4SDimitry Andric         "Source", nullptr, 's', ApplicationDelegate::eMenuID_ViewSource)));
7640480093f4SDimitry Andric     view_menu_sp->AddSubmenu(
7641480093f4SDimitry Andric         MenuSP(new Menu("Variables", nullptr, 'v',
7642480093f4SDimitry Andric                         ApplicationDelegate::eMenuID_ViewVariables)));
7643349cc55cSDimitry Andric     view_menu_sp->AddSubmenu(
7644349cc55cSDimitry Andric         MenuSP(new Menu("Breakpoints", nullptr, 'b',
7645349cc55cSDimitry Andric                         ApplicationDelegate::eMenuID_ViewBreakpoints)));
7646480093f4SDimitry Andric 
7647480093f4SDimitry Andric     MenuSP help_menu_sp(
7648480093f4SDimitry Andric         new Menu("Help", "F6", KEY_F(6), ApplicationDelegate::eMenuID_Help));
7649480093f4SDimitry Andric     help_menu_sp->AddSubmenu(MenuSP(new Menu(
7650480093f4SDimitry Andric         "GUI Help", nullptr, 'g', ApplicationDelegate::eMenuID_HelpGUIHelp)));
7651480093f4SDimitry Andric 
7652480093f4SDimitry Andric     m_app_ap->Initialize();
7653480093f4SDimitry Andric     WindowSP &main_window_sp = m_app_ap->GetMainWindow();
7654480093f4SDimitry Andric 
7655480093f4SDimitry Andric     MenuSP menubar_sp(new Menu(Menu::Type::Bar));
7656480093f4SDimitry Andric     menubar_sp->AddSubmenu(lldb_menu_sp);
7657480093f4SDimitry Andric     menubar_sp->AddSubmenu(target_menu_sp);
7658480093f4SDimitry Andric     menubar_sp->AddSubmenu(process_menu_sp);
7659480093f4SDimitry Andric     menubar_sp->AddSubmenu(thread_menu_sp);
7660480093f4SDimitry Andric     menubar_sp->AddSubmenu(view_menu_sp);
7661480093f4SDimitry Andric     menubar_sp->AddSubmenu(help_menu_sp);
7662480093f4SDimitry Andric     menubar_sp->SetDelegate(app_menu_delegate_sp);
7663480093f4SDimitry Andric 
7664480093f4SDimitry Andric     Rect content_bounds = main_window_sp->GetFrame();
7665480093f4SDimitry Andric     Rect menubar_bounds = content_bounds.MakeMenuBar();
7666480093f4SDimitry Andric     Rect status_bounds = content_bounds.MakeStatusBar();
7667480093f4SDimitry Andric     Rect source_bounds;
7668480093f4SDimitry Andric     Rect variables_bounds;
7669480093f4SDimitry Andric     Rect threads_bounds;
7670480093f4SDimitry Andric     Rect source_variables_bounds;
7671480093f4SDimitry Andric     content_bounds.VerticalSplitPercentage(0.80, source_variables_bounds,
7672480093f4SDimitry Andric                                            threads_bounds);
7673480093f4SDimitry Andric     source_variables_bounds.HorizontalSplitPercentage(0.70, source_bounds,
7674480093f4SDimitry Andric                                                       variables_bounds);
7675480093f4SDimitry Andric 
7676480093f4SDimitry Andric     WindowSP menubar_window_sp =
7677480093f4SDimitry Andric         main_window_sp->CreateSubWindow("Menubar", menubar_bounds, false);
7678480093f4SDimitry Andric     // Let the menubar get keys if the active window doesn't handle the keys
7679480093f4SDimitry Andric     // that are typed so it can respond to menubar key presses.
7680480093f4SDimitry Andric     menubar_window_sp->SetCanBeActive(
7681480093f4SDimitry Andric         false); // Don't let the menubar become the active window
7682480093f4SDimitry Andric     menubar_window_sp->SetDelegate(menubar_sp);
7683480093f4SDimitry Andric 
7684480093f4SDimitry Andric     WindowSP source_window_sp(
7685480093f4SDimitry Andric         main_window_sp->CreateSubWindow("Source", source_bounds, true));
7686480093f4SDimitry Andric     WindowSP variables_window_sp(
7687480093f4SDimitry Andric         main_window_sp->CreateSubWindow("Variables", variables_bounds, false));
7688480093f4SDimitry Andric     WindowSP threads_window_sp(
7689480093f4SDimitry Andric         main_window_sp->CreateSubWindow("Threads", threads_bounds, false));
7690480093f4SDimitry Andric     WindowSP status_window_sp(
7691480093f4SDimitry Andric         main_window_sp->CreateSubWindow("Status", status_bounds, false));
7692480093f4SDimitry Andric     status_window_sp->SetCanBeActive(
7693480093f4SDimitry Andric         false); // Don't let the status bar become the active window
7694480093f4SDimitry Andric     main_window_sp->SetDelegate(
7695480093f4SDimitry Andric         std::static_pointer_cast<WindowDelegate>(app_delegate_sp));
7696480093f4SDimitry Andric     source_window_sp->SetDelegate(
7697480093f4SDimitry Andric         WindowDelegateSP(new SourceFileWindowDelegate(m_debugger)));
7698480093f4SDimitry Andric     variables_window_sp->SetDelegate(
7699480093f4SDimitry Andric         WindowDelegateSP(new FrameVariablesWindowDelegate(m_debugger)));
7700480093f4SDimitry Andric     TreeDelegateSP thread_delegate_sp(new ThreadsTreeDelegate(m_debugger));
7701480093f4SDimitry Andric     threads_window_sp->SetDelegate(WindowDelegateSP(
7702480093f4SDimitry Andric         new TreeWindowDelegate(m_debugger, thread_delegate_sp)));
7703480093f4SDimitry Andric     status_window_sp->SetDelegate(
7704480093f4SDimitry Andric         WindowDelegateSP(new StatusBarWindowDelegate(m_debugger)));
7705480093f4SDimitry Andric 
7706e8d8bef9SDimitry Andric     // All colors with black background.
7707e8d8bef9SDimitry Andric     init_pair(1, COLOR_BLACK, COLOR_BLACK);
7708e8d8bef9SDimitry Andric     init_pair(2, COLOR_RED, COLOR_BLACK);
7709e8d8bef9SDimitry Andric     init_pair(3, COLOR_GREEN, COLOR_BLACK);
7710e8d8bef9SDimitry Andric     init_pair(4, COLOR_YELLOW, COLOR_BLACK);
7711e8d8bef9SDimitry Andric     init_pair(5, COLOR_BLUE, COLOR_BLACK);
7712e8d8bef9SDimitry Andric     init_pair(6, COLOR_MAGENTA, COLOR_BLACK);
7713e8d8bef9SDimitry Andric     init_pair(7, COLOR_CYAN, COLOR_BLACK);
7714e8d8bef9SDimitry Andric     init_pair(8, COLOR_WHITE, COLOR_BLACK);
7715e8d8bef9SDimitry Andric     // All colors with blue background.
7716e8d8bef9SDimitry Andric     init_pair(9, COLOR_BLACK, COLOR_BLUE);
7717e8d8bef9SDimitry Andric     init_pair(10, COLOR_RED, COLOR_BLUE);
7718e8d8bef9SDimitry Andric     init_pair(11, COLOR_GREEN, COLOR_BLUE);
7719e8d8bef9SDimitry Andric     init_pair(12, COLOR_YELLOW, COLOR_BLUE);
7720e8d8bef9SDimitry Andric     init_pair(13, COLOR_BLUE, COLOR_BLUE);
7721e8d8bef9SDimitry Andric     init_pair(14, COLOR_MAGENTA, COLOR_BLUE);
7722e8d8bef9SDimitry Andric     init_pair(15, COLOR_CYAN, COLOR_BLUE);
7723e8d8bef9SDimitry Andric     init_pair(16, COLOR_WHITE, COLOR_BLUE);
7724e8d8bef9SDimitry Andric     // These must match the order in the color indexes enum.
7725e8d8bef9SDimitry Andric     init_pair(17, COLOR_BLACK, COLOR_WHITE);
7726e8d8bef9SDimitry Andric     init_pair(18, COLOR_MAGENTA, COLOR_WHITE);
7727e8d8bef9SDimitry Andric     static_assert(LastColorPairIndex == 18, "Color indexes do not match.");
7728fe6060f1SDimitry Andric 
7729fe6060f1SDimitry Andric     define_key("\033[Z", KEY_SHIFT_TAB);
7730349cc55cSDimitry Andric     define_key("\033\015", KEY_ALT_ENTER);
7731480093f4SDimitry Andric   }
7732480093f4SDimitry Andric }
7733480093f4SDimitry Andric 
Deactivate()7734480093f4SDimitry Andric void IOHandlerCursesGUI::Deactivate() { m_app_ap->Terminate(); }
7735480093f4SDimitry Andric 
Run()7736480093f4SDimitry Andric void IOHandlerCursesGUI::Run() {
7737480093f4SDimitry Andric   m_app_ap->Run(m_debugger);
7738480093f4SDimitry Andric   SetIsDone(true);
7739480093f4SDimitry Andric }
7740480093f4SDimitry Andric 
7741480093f4SDimitry Andric IOHandlerCursesGUI::~IOHandlerCursesGUI() = default;
7742480093f4SDimitry Andric 
Cancel()7743480093f4SDimitry Andric void IOHandlerCursesGUI::Cancel() {}
7744480093f4SDimitry Andric 
Interrupt()774581ad6265SDimitry Andric bool IOHandlerCursesGUI::Interrupt() {
774681ad6265SDimitry Andric   return m_debugger.GetCommandInterpreter().IOHandlerInterrupt(*this);
774781ad6265SDimitry Andric }
7748480093f4SDimitry Andric 
GotEOF()7749480093f4SDimitry Andric void IOHandlerCursesGUI::GotEOF() {}
7750480093f4SDimitry Andric 
TerminalSizeChanged()7751e8d8bef9SDimitry Andric void IOHandlerCursesGUI::TerminalSizeChanged() {
7752e8d8bef9SDimitry Andric   m_app_ap->TerminalSizeChanged();
7753e8d8bef9SDimitry Andric }
7754e8d8bef9SDimitry Andric 
7755480093f4SDimitry Andric #endif // LLDB_ENABLE_CURSES
7756