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"
28480093f4SDimitry Andric #include "lldb/Core/StreamFile.h"
29fe6060f1SDimitry Andric #include "lldb/Core/ValueObjectUpdater.h"
30480093f4SDimitry Andric #include "lldb/Host/File.h"
3181ad6265SDimitry Andric #include "lldb/Utility/AnsiTerminal.h"
32480093f4SDimitry Andric #include "lldb/Utility/Predicate.h"
33480093f4SDimitry Andric #include "lldb/Utility/Status.h"
34480093f4SDimitry Andric #include "lldb/Utility/StreamString.h"
35480093f4SDimitry Andric #include "lldb/Utility/StringList.h"
36480093f4SDimitry Andric #include "lldb/lldb-forward.h"
37480093f4SDimitry Andric 
38480093f4SDimitry Andric #include "lldb/Interpreter/CommandCompletions.h"
39480093f4SDimitry Andric #include "lldb/Interpreter/CommandInterpreter.h"
40349cc55cSDimitry Andric #include "lldb/Interpreter/OptionGroupPlatform.h"
41480093f4SDimitry Andric 
42480093f4SDimitry Andric #if LLDB_ENABLE_CURSES
43480093f4SDimitry Andric #include "lldb/Breakpoint/BreakpointLocation.h"
44480093f4SDimitry Andric #include "lldb/Core/Module.h"
45fe6060f1SDimitry Andric #include "lldb/Core/PluginManager.h"
46480093f4SDimitry Andric #include "lldb/Core/ValueObject.h"
47480093f4SDimitry Andric #include "lldb/Core/ValueObjectRegister.h"
48480093f4SDimitry Andric #include "lldb/Symbol/Block.h"
49349cc55cSDimitry Andric #include "lldb/Symbol/CompileUnit.h"
50480093f4SDimitry Andric #include "lldb/Symbol/Function.h"
51480093f4SDimitry Andric #include "lldb/Symbol/Symbol.h"
52480093f4SDimitry Andric #include "lldb/Symbol/VariableList.h"
53480093f4SDimitry Andric #include "lldb/Target/Process.h"
54480093f4SDimitry Andric #include "lldb/Target/RegisterContext.h"
55480093f4SDimitry Andric #include "lldb/Target/StackFrame.h"
56480093f4SDimitry Andric #include "lldb/Target/StopInfo.h"
57480093f4SDimitry Andric #include "lldb/Target/Target.h"
58480093f4SDimitry Andric #include "lldb/Target/Thread.h"
59480093f4SDimitry Andric #include "lldb/Utility/State.h"
60480093f4SDimitry Andric #endif
61480093f4SDimitry Andric 
62480093f4SDimitry Andric #include "llvm/ADT/StringRef.h"
63480093f4SDimitry Andric 
64480093f4SDimitry Andric #ifdef _WIN32
65480093f4SDimitry Andric #include "lldb/Host/windows/windows.h"
66480093f4SDimitry Andric #endif
67480093f4SDimitry Andric 
68480093f4SDimitry Andric #include <memory>
69480093f4SDimitry Andric #include <mutex>
70480093f4SDimitry Andric 
71fe6060f1SDimitry Andric #include <cassert>
72fe6060f1SDimitry Andric #include <cctype>
73fe6060f1SDimitry Andric #include <cerrno>
74fe6060f1SDimitry Andric #include <cstdint>
75fe6060f1SDimitry Andric #include <cstdio>
76fe6060f1SDimitry Andric #include <cstring>
77fe6060f1SDimitry Andric #include <functional>
78bdd1243dSDimitry Andric #include <optional>
79480093f4SDimitry Andric #include <type_traits>
80480093f4SDimitry Andric 
81480093f4SDimitry Andric using namespace lldb;
82480093f4SDimitry Andric using namespace lldb_private;
83480093f4SDimitry Andric using llvm::StringRef;
84480093f4SDimitry Andric 
85480093f4SDimitry Andric // we may want curses to be disabled for some builds for instance, windows
86480093f4SDimitry Andric #if LLDB_ENABLE_CURSES
87480093f4SDimitry Andric 
88349cc55cSDimitry Andric #define KEY_CTRL_A 1
89349cc55cSDimitry Andric #define KEY_CTRL_E 5
90349cc55cSDimitry Andric #define KEY_CTRL_K 11
91480093f4SDimitry Andric #define KEY_RETURN 10
92480093f4SDimitry Andric #define KEY_ESCAPE 27
93349cc55cSDimitry Andric #define KEY_DELETE 127
94480093f4SDimitry Andric 
95fe6060f1SDimitry Andric #define KEY_SHIFT_TAB (KEY_MAX + 1)
96349cc55cSDimitry Andric #define KEY_ALT_ENTER (KEY_MAX + 2)
97fe6060f1SDimitry Andric 
98480093f4SDimitry Andric namespace curses {
99480093f4SDimitry Andric class Menu;
100480093f4SDimitry Andric class MenuDelegate;
101480093f4SDimitry Andric class Window;
102480093f4SDimitry Andric class WindowDelegate;
103480093f4SDimitry Andric typedef std::shared_ptr<Menu> MenuSP;
104480093f4SDimitry Andric typedef std::shared_ptr<MenuDelegate> MenuDelegateSP;
105480093f4SDimitry Andric typedef std::shared_ptr<Window> WindowSP;
106480093f4SDimitry Andric typedef std::shared_ptr<WindowDelegate> WindowDelegateSP;
107480093f4SDimitry Andric typedef std::vector<MenuSP> Menus;
108480093f4SDimitry Andric typedef std::vector<WindowSP> Windows;
109480093f4SDimitry Andric typedef std::vector<WindowDelegateSP> WindowDelegates;
110480093f4SDimitry Andric 
111480093f4SDimitry Andric #if 0
112480093f4SDimitry Andric type summary add -s "x=${var.x}, y=${var.y}" curses::Point
113480093f4SDimitry Andric type summary add -s "w=${var.width}, h=${var.height}" curses::Size
114480093f4SDimitry Andric type summary add -s "${var.origin%S} ${var.size%S}" curses::Rect
115480093f4SDimitry Andric #endif
116480093f4SDimitry Andric 
117480093f4SDimitry Andric struct Point {
118480093f4SDimitry Andric   int x;
119480093f4SDimitry Andric   int y;
120480093f4SDimitry Andric 
121480093f4SDimitry Andric   Point(int _x = 0, int _y = 0) : x(_x), y(_y) {}
122480093f4SDimitry Andric 
123480093f4SDimitry Andric   void Clear() {
124480093f4SDimitry Andric     x = 0;
125480093f4SDimitry Andric     y = 0;
126480093f4SDimitry Andric   }
127480093f4SDimitry Andric 
128480093f4SDimitry Andric   Point &operator+=(const Point &rhs) {
129480093f4SDimitry Andric     x += rhs.x;
130480093f4SDimitry Andric     y += rhs.y;
131480093f4SDimitry Andric     return *this;
132480093f4SDimitry Andric   }
133480093f4SDimitry Andric 
134480093f4SDimitry Andric   void Dump() { printf("(x=%i, y=%i)\n", x, y); }
135480093f4SDimitry Andric };
136480093f4SDimitry Andric 
137480093f4SDimitry Andric bool operator==(const Point &lhs, const Point &rhs) {
138480093f4SDimitry Andric   return lhs.x == rhs.x && lhs.y == rhs.y;
139480093f4SDimitry Andric }
140480093f4SDimitry Andric 
141480093f4SDimitry Andric bool operator!=(const Point &lhs, const Point &rhs) {
142480093f4SDimitry Andric   return lhs.x != rhs.x || lhs.y != rhs.y;
143480093f4SDimitry Andric }
144480093f4SDimitry Andric 
145480093f4SDimitry Andric struct Size {
146480093f4SDimitry Andric   int width;
147480093f4SDimitry Andric   int height;
148480093f4SDimitry Andric   Size(int w = 0, int h = 0) : width(w), height(h) {}
149480093f4SDimitry Andric 
150480093f4SDimitry Andric   void Clear() {
151480093f4SDimitry Andric     width = 0;
152480093f4SDimitry Andric     height = 0;
153480093f4SDimitry Andric   }
154480093f4SDimitry Andric 
155480093f4SDimitry Andric   void Dump() { printf("(w=%i, h=%i)\n", width, height); }
156480093f4SDimitry Andric };
157480093f4SDimitry Andric 
158480093f4SDimitry Andric bool operator==(const Size &lhs, const Size &rhs) {
159480093f4SDimitry Andric   return lhs.width == rhs.width && lhs.height == rhs.height;
160480093f4SDimitry Andric }
161480093f4SDimitry Andric 
162480093f4SDimitry Andric bool operator!=(const Size &lhs, const Size &rhs) {
163480093f4SDimitry Andric   return lhs.width != rhs.width || lhs.height != rhs.height;
164480093f4SDimitry Andric }
165480093f4SDimitry Andric 
166480093f4SDimitry Andric struct Rect {
167480093f4SDimitry Andric   Point origin;
168480093f4SDimitry Andric   Size size;
169480093f4SDimitry Andric 
170480093f4SDimitry Andric   Rect() : origin(), size() {}
171480093f4SDimitry Andric 
172480093f4SDimitry Andric   Rect(const Point &p, const Size &s) : origin(p), size(s) {}
173480093f4SDimitry Andric 
174480093f4SDimitry Andric   void Clear() {
175480093f4SDimitry Andric     origin.Clear();
176480093f4SDimitry Andric     size.Clear();
177480093f4SDimitry Andric   }
178480093f4SDimitry Andric 
179480093f4SDimitry Andric   void Dump() {
180480093f4SDimitry Andric     printf("(x=%i, y=%i), w=%i, h=%i)\n", origin.x, origin.y, size.width,
181480093f4SDimitry Andric            size.height);
182480093f4SDimitry Andric   }
183480093f4SDimitry Andric 
184480093f4SDimitry Andric   void Inset(int w, int h) {
185480093f4SDimitry Andric     if (size.width > w * 2)
186480093f4SDimitry Andric       size.width -= w * 2;
187480093f4SDimitry Andric     origin.x += w;
188480093f4SDimitry Andric 
189480093f4SDimitry Andric     if (size.height > h * 2)
190480093f4SDimitry Andric       size.height -= h * 2;
191480093f4SDimitry Andric     origin.y += h;
192480093f4SDimitry Andric   }
193480093f4SDimitry Andric 
194480093f4SDimitry Andric   // Return a status bar rectangle which is the last line of this rectangle.
195480093f4SDimitry Andric   // This rectangle will be modified to not include the status bar area.
196480093f4SDimitry Andric   Rect MakeStatusBar() {
197480093f4SDimitry Andric     Rect status_bar;
198480093f4SDimitry Andric     if (size.height > 1) {
199480093f4SDimitry Andric       status_bar.origin.x = origin.x;
200480093f4SDimitry Andric       status_bar.origin.y = size.height;
201480093f4SDimitry Andric       status_bar.size.width = size.width;
202480093f4SDimitry Andric       status_bar.size.height = 1;
203480093f4SDimitry Andric       --size.height;
204480093f4SDimitry Andric     }
205480093f4SDimitry Andric     return status_bar;
206480093f4SDimitry Andric   }
207480093f4SDimitry Andric 
208480093f4SDimitry Andric   // Return a menubar rectangle which is the first line of this rectangle. This
209480093f4SDimitry Andric   // rectangle will be modified to not include the menubar area.
210480093f4SDimitry Andric   Rect MakeMenuBar() {
211480093f4SDimitry Andric     Rect menubar;
212480093f4SDimitry Andric     if (size.height > 1) {
213480093f4SDimitry Andric       menubar.origin.x = origin.x;
214480093f4SDimitry Andric       menubar.origin.y = origin.y;
215480093f4SDimitry Andric       menubar.size.width = size.width;
216480093f4SDimitry Andric       menubar.size.height = 1;
217480093f4SDimitry Andric       ++origin.y;
218480093f4SDimitry Andric       --size.height;
219480093f4SDimitry Andric     }
220480093f4SDimitry Andric     return menubar;
221480093f4SDimitry Andric   }
222480093f4SDimitry Andric 
223480093f4SDimitry Andric   void HorizontalSplitPercentage(float top_percentage, Rect &top,
224480093f4SDimitry Andric                                  Rect &bottom) const {
225480093f4SDimitry Andric     float top_height = top_percentage * size.height;
226480093f4SDimitry Andric     HorizontalSplit(top_height, top, bottom);
227480093f4SDimitry Andric   }
228480093f4SDimitry Andric 
229480093f4SDimitry Andric   void HorizontalSplit(int top_height, Rect &top, Rect &bottom) const {
230480093f4SDimitry Andric     top = *this;
231480093f4SDimitry Andric     if (top_height < size.height) {
232480093f4SDimitry Andric       top.size.height = top_height;
233480093f4SDimitry Andric       bottom.origin.x = origin.x;
234480093f4SDimitry Andric       bottom.origin.y = origin.y + top.size.height;
235480093f4SDimitry Andric       bottom.size.width = size.width;
236480093f4SDimitry Andric       bottom.size.height = size.height - top.size.height;
237480093f4SDimitry Andric     } else {
238480093f4SDimitry Andric       bottom.Clear();
239480093f4SDimitry Andric     }
240480093f4SDimitry Andric   }
241480093f4SDimitry Andric 
242480093f4SDimitry Andric   void VerticalSplitPercentage(float left_percentage, Rect &left,
243480093f4SDimitry Andric                                Rect &right) const {
244480093f4SDimitry Andric     float left_width = left_percentage * size.width;
245480093f4SDimitry Andric     VerticalSplit(left_width, left, right);
246480093f4SDimitry Andric   }
247480093f4SDimitry Andric 
248480093f4SDimitry Andric   void VerticalSplit(int left_width, Rect &left, Rect &right) const {
249480093f4SDimitry Andric     left = *this;
250480093f4SDimitry Andric     if (left_width < size.width) {
251480093f4SDimitry Andric       left.size.width = left_width;
252480093f4SDimitry Andric       right.origin.x = origin.x + left.size.width;
253480093f4SDimitry Andric       right.origin.y = origin.y;
254480093f4SDimitry Andric       right.size.width = size.width - left.size.width;
255480093f4SDimitry Andric       right.size.height = size.height;
256480093f4SDimitry Andric     } else {
257480093f4SDimitry Andric       right.Clear();
258480093f4SDimitry Andric     }
259480093f4SDimitry Andric   }
260480093f4SDimitry Andric };
261480093f4SDimitry Andric 
262480093f4SDimitry Andric bool operator==(const Rect &lhs, const Rect &rhs) {
263480093f4SDimitry Andric   return lhs.origin == rhs.origin && lhs.size == rhs.size;
264480093f4SDimitry Andric }
265480093f4SDimitry Andric 
266480093f4SDimitry Andric bool operator!=(const Rect &lhs, const Rect &rhs) {
267480093f4SDimitry Andric   return lhs.origin != rhs.origin || lhs.size != rhs.size;
268480093f4SDimitry Andric }
269480093f4SDimitry Andric 
270480093f4SDimitry Andric enum HandleCharResult {
271480093f4SDimitry Andric   eKeyNotHandled = 0,
272480093f4SDimitry Andric   eKeyHandled = 1,
273480093f4SDimitry Andric   eQuitApplication = 2
274480093f4SDimitry Andric };
275480093f4SDimitry Andric 
276480093f4SDimitry Andric enum class MenuActionResult {
277480093f4SDimitry Andric   Handled,
278480093f4SDimitry Andric   NotHandled,
279480093f4SDimitry Andric   Quit // Exit all menus and quit
280480093f4SDimitry Andric };
281480093f4SDimitry Andric 
282480093f4SDimitry Andric struct KeyHelp {
283480093f4SDimitry Andric   int ch;
284480093f4SDimitry Andric   const char *description;
285480093f4SDimitry Andric };
286480093f4SDimitry Andric 
287e8d8bef9SDimitry Andric // COLOR_PAIR index names
288e8d8bef9SDimitry Andric enum {
289e8d8bef9SDimitry Andric   // First 16 colors are 8 black background and 8 blue background colors,
290e8d8bef9SDimitry Andric   // needed by OutputColoredStringTruncated().
291e8d8bef9SDimitry Andric   BlackOnBlack = 1,
292e8d8bef9SDimitry Andric   RedOnBlack,
293e8d8bef9SDimitry Andric   GreenOnBlack,
294e8d8bef9SDimitry Andric   YellowOnBlack,
295e8d8bef9SDimitry Andric   BlueOnBlack,
296e8d8bef9SDimitry Andric   MagentaOnBlack,
297e8d8bef9SDimitry Andric   CyanOnBlack,
298e8d8bef9SDimitry Andric   WhiteOnBlack,
299e8d8bef9SDimitry Andric   BlackOnBlue,
300e8d8bef9SDimitry Andric   RedOnBlue,
301e8d8bef9SDimitry Andric   GreenOnBlue,
302e8d8bef9SDimitry Andric   YellowOnBlue,
303e8d8bef9SDimitry Andric   BlueOnBlue,
304e8d8bef9SDimitry Andric   MagentaOnBlue,
305e8d8bef9SDimitry Andric   CyanOnBlue,
306e8d8bef9SDimitry Andric   WhiteOnBlue,
307e8d8bef9SDimitry Andric   // Other colors, as needed.
308e8d8bef9SDimitry Andric   BlackOnWhite,
309e8d8bef9SDimitry Andric   MagentaOnWhite,
310e8d8bef9SDimitry Andric   LastColorPairIndex = MagentaOnWhite
311e8d8bef9SDimitry Andric };
312e8d8bef9SDimitry Andric 
313480093f4SDimitry Andric class WindowDelegate {
314480093f4SDimitry Andric public:
315480093f4SDimitry Andric   virtual ~WindowDelegate() = default;
316480093f4SDimitry Andric 
317480093f4SDimitry Andric   virtual bool WindowDelegateDraw(Window &window, bool force) {
318480093f4SDimitry Andric     return false; // Drawing not handled
319480093f4SDimitry Andric   }
320480093f4SDimitry Andric 
321480093f4SDimitry Andric   virtual HandleCharResult WindowDelegateHandleChar(Window &window, int key) {
322480093f4SDimitry Andric     return eKeyNotHandled;
323480093f4SDimitry Andric   }
324480093f4SDimitry Andric 
325480093f4SDimitry Andric   virtual const char *WindowDelegateGetHelpText() { return nullptr; }
326480093f4SDimitry Andric 
327480093f4SDimitry Andric   virtual KeyHelp *WindowDelegateGetKeyHelp() { return nullptr; }
328480093f4SDimitry Andric };
329480093f4SDimitry Andric 
330480093f4SDimitry Andric class HelpDialogDelegate : public WindowDelegate {
331480093f4SDimitry Andric public:
332480093f4SDimitry Andric   HelpDialogDelegate(const char *text, KeyHelp *key_help_array);
333480093f4SDimitry Andric 
334480093f4SDimitry Andric   ~HelpDialogDelegate() override;
335480093f4SDimitry Andric 
336480093f4SDimitry Andric   bool WindowDelegateDraw(Window &window, bool force) override;
337480093f4SDimitry Andric 
338480093f4SDimitry Andric   HandleCharResult WindowDelegateHandleChar(Window &window, int key) override;
339480093f4SDimitry Andric 
340480093f4SDimitry Andric   size_t GetNumLines() const { return m_text.GetSize(); }
341480093f4SDimitry Andric 
342480093f4SDimitry Andric   size_t GetMaxLineLength() const { return m_text.GetMaxStringLength(); }
343480093f4SDimitry Andric 
344480093f4SDimitry Andric protected:
345480093f4SDimitry Andric   StringList m_text;
34681ad6265SDimitry Andric   int m_first_visible_line = 0;
347480093f4SDimitry Andric };
348480093f4SDimitry Andric 
349fe6060f1SDimitry Andric // A surface is an abstraction for something than can be drawn on. The surface
350fe6060f1SDimitry Andric // have a width, a height, a cursor position, and a multitude of drawing
351fe6060f1SDimitry Andric // operations. This type should be sub-classed to get an actually useful ncurses
352349cc55cSDimitry Andric // object, such as a Window or a Pad.
353fe6060f1SDimitry Andric class Surface {
354480093f4SDimitry Andric public:
355349cc55cSDimitry Andric   enum class Type { Window, Pad };
356349cc55cSDimitry Andric 
35781ad6265SDimitry Andric   Surface(Surface::Type type) : m_type(type) {}
358480093f4SDimitry Andric 
359fe6060f1SDimitry Andric   WINDOW *get() { return m_window; }
360fe6060f1SDimitry Andric 
361fe6060f1SDimitry Andric   operator WINDOW *() { return m_window; }
362fe6060f1SDimitry Andric 
363349cc55cSDimitry Andric   Surface SubSurface(Rect bounds) {
364349cc55cSDimitry Andric     Surface subSurface(m_type);
365349cc55cSDimitry Andric     if (m_type == Type::Pad)
366349cc55cSDimitry Andric       subSurface.m_window =
367349cc55cSDimitry Andric           ::subpad(m_window, bounds.size.height, bounds.size.width,
368349cc55cSDimitry Andric                    bounds.origin.y, bounds.origin.x);
369349cc55cSDimitry Andric     else
370349cc55cSDimitry Andric       subSurface.m_window =
371349cc55cSDimitry Andric           ::derwin(m_window, bounds.size.height, bounds.size.width,
372349cc55cSDimitry Andric                    bounds.origin.y, bounds.origin.x);
373349cc55cSDimitry Andric     return subSurface;
374349cc55cSDimitry Andric   }
375349cc55cSDimitry Andric 
376fe6060f1SDimitry Andric   // Copy a region of the surface to another surface.
377fe6060f1SDimitry Andric   void CopyToSurface(Surface &target, Point source_origin, Point target_origin,
378fe6060f1SDimitry Andric                      Size size) {
379fe6060f1SDimitry Andric     ::copywin(m_window, target.get(), source_origin.y, source_origin.x,
380fe6060f1SDimitry Andric               target_origin.y, target_origin.x,
381fe6060f1SDimitry Andric               target_origin.y + size.height - 1,
382fe6060f1SDimitry Andric               target_origin.x + size.width - 1, false);
383480093f4SDimitry Andric   }
384480093f4SDimitry Andric 
385fe6060f1SDimitry Andric   int GetCursorX() const { return getcurx(m_window); }
386fe6060f1SDimitry Andric   int GetCursorY() const { return getcury(m_window); }
387fe6060f1SDimitry Andric   void MoveCursor(int x, int y) { ::wmove(m_window, y, x); }
388480093f4SDimitry Andric 
389480093f4SDimitry Andric   void AttributeOn(attr_t attr) { ::wattron(m_window, attr); }
390480093f4SDimitry Andric   void AttributeOff(attr_t attr) { ::wattroff(m_window, attr); }
391fe6060f1SDimitry Andric 
392e8d8bef9SDimitry Andric   int GetMaxX() const { return getmaxx(m_window); }
393e8d8bef9SDimitry Andric   int GetMaxY() const { return getmaxy(m_window); }
394e8d8bef9SDimitry Andric   int GetWidth() const { return GetMaxX(); }
395e8d8bef9SDimitry Andric   int GetHeight() const { return GetMaxY(); }
396fe6060f1SDimitry Andric   Size GetSize() const { return Size(GetWidth(), GetHeight()); }
397fe6060f1SDimitry Andric   // Get a zero origin rectangle width the surface size.
398fe6060f1SDimitry Andric   Rect GetFrame() const { return Rect(Point(), GetSize()); }
399fe6060f1SDimitry Andric 
400fe6060f1SDimitry Andric   void Clear() { ::wclear(m_window); }
401fe6060f1SDimitry Andric   void Erase() { ::werase(m_window); }
402fe6060f1SDimitry Andric 
403480093f4SDimitry Andric   void SetBackground(int color_pair_idx) {
404480093f4SDimitry Andric     ::wbkgd(m_window, COLOR_PAIR(color_pair_idx));
405480093f4SDimitry Andric   }
406480093f4SDimitry Andric 
407fe6060f1SDimitry Andric   void PutChar(int ch) { ::waddch(m_window, ch); }
408fe6060f1SDimitry Andric   void PutCString(const char *s, int len = -1) { ::waddnstr(m_window, s, len); }
409fe6060f1SDimitry Andric 
410e8d8bef9SDimitry Andric   void PutCStringTruncated(int right_pad, const char *s, int len = -1) {
411480093f4SDimitry Andric     int bytes_left = GetWidth() - GetCursorX();
412480093f4SDimitry Andric     if (bytes_left > right_pad) {
413480093f4SDimitry Andric       bytes_left -= right_pad;
414e8d8bef9SDimitry Andric       ::waddnstr(m_window, s, len < 0 ? bytes_left : std::min(bytes_left, len));
415480093f4SDimitry Andric     }
416480093f4SDimitry Andric   }
417480093f4SDimitry Andric 
418480093f4SDimitry Andric   void Printf(const char *format, ...) __attribute__((format(printf, 2, 3))) {
419480093f4SDimitry Andric     va_list args;
420480093f4SDimitry Andric     va_start(args, format);
421fe6060f1SDimitry Andric     vw_printw(m_window, format, args);
422480093f4SDimitry Andric     va_end(args);
423480093f4SDimitry Andric   }
424480093f4SDimitry Andric 
425e8d8bef9SDimitry Andric   void PrintfTruncated(int right_pad, const char *format, ...)
426e8d8bef9SDimitry Andric       __attribute__((format(printf, 3, 4))) {
427e8d8bef9SDimitry Andric     va_list args;
428e8d8bef9SDimitry Andric     va_start(args, format);
429e8d8bef9SDimitry Andric     StreamString strm;
430e8d8bef9SDimitry Andric     strm.PrintfVarArg(format, args);
431e8d8bef9SDimitry Andric     va_end(args);
432e8d8bef9SDimitry Andric     PutCStringTruncated(right_pad, strm.GetData());
433e8d8bef9SDimitry Andric   }
434e8d8bef9SDimitry Andric 
435fe6060f1SDimitry Andric   void VerticalLine(int n, chtype v_char = ACS_VLINE) {
436fe6060f1SDimitry Andric     ::wvline(m_window, v_char, n);
437fe6060f1SDimitry Andric   }
438fe6060f1SDimitry Andric   void HorizontalLine(int n, chtype h_char = ACS_HLINE) {
439fe6060f1SDimitry Andric     ::whline(m_window, h_char, n);
440fe6060f1SDimitry Andric   }
441fe6060f1SDimitry Andric   void Box(chtype v_char = ACS_VLINE, chtype h_char = ACS_HLINE) {
442fe6060f1SDimitry Andric     ::box(m_window, v_char, h_char);
443fe6060f1SDimitry Andric   }
444fe6060f1SDimitry Andric 
445fe6060f1SDimitry Andric   void TitledBox(const char *title, chtype v_char = ACS_VLINE,
446fe6060f1SDimitry Andric                  chtype h_char = ACS_HLINE) {
447fe6060f1SDimitry Andric     Box(v_char, h_char);
448fe6060f1SDimitry Andric     int title_offset = 2;
449fe6060f1SDimitry Andric     MoveCursor(title_offset, 0);
450fe6060f1SDimitry Andric     PutChar('[');
451fe6060f1SDimitry Andric     PutCString(title, GetWidth() - title_offset);
452fe6060f1SDimitry Andric     PutChar(']');
453fe6060f1SDimitry Andric   }
454fe6060f1SDimitry Andric 
455fe6060f1SDimitry Andric   void Box(const Rect &bounds, chtype v_char = ACS_VLINE,
456fe6060f1SDimitry Andric            chtype h_char = ACS_HLINE) {
457fe6060f1SDimitry Andric     MoveCursor(bounds.origin.x, bounds.origin.y);
458fe6060f1SDimitry Andric     VerticalLine(bounds.size.height);
459fe6060f1SDimitry Andric     HorizontalLine(bounds.size.width);
460fe6060f1SDimitry Andric     PutChar(ACS_ULCORNER);
461fe6060f1SDimitry Andric 
462fe6060f1SDimitry Andric     MoveCursor(bounds.origin.x + bounds.size.width - 1, bounds.origin.y);
463fe6060f1SDimitry Andric     VerticalLine(bounds.size.height);
464fe6060f1SDimitry Andric     PutChar(ACS_URCORNER);
465fe6060f1SDimitry Andric 
466fe6060f1SDimitry Andric     MoveCursor(bounds.origin.x, bounds.origin.y + bounds.size.height - 1);
467fe6060f1SDimitry Andric     HorizontalLine(bounds.size.width);
468fe6060f1SDimitry Andric     PutChar(ACS_LLCORNER);
469fe6060f1SDimitry Andric 
470fe6060f1SDimitry Andric     MoveCursor(bounds.origin.x + bounds.size.width - 1,
471fe6060f1SDimitry Andric                bounds.origin.y + bounds.size.height - 1);
472fe6060f1SDimitry Andric     PutChar(ACS_LRCORNER);
473fe6060f1SDimitry Andric   }
474fe6060f1SDimitry Andric 
475fe6060f1SDimitry Andric   void TitledBox(const Rect &bounds, const char *title,
476fe6060f1SDimitry Andric                  chtype v_char = ACS_VLINE, chtype h_char = ACS_HLINE) {
477fe6060f1SDimitry Andric     Box(bounds, v_char, h_char);
478fe6060f1SDimitry Andric     int title_offset = 2;
479fe6060f1SDimitry Andric     MoveCursor(bounds.origin.x + title_offset, bounds.origin.y);
480fe6060f1SDimitry Andric     PutChar('[');
481fe6060f1SDimitry Andric     PutCString(title, bounds.size.width - title_offset);
482fe6060f1SDimitry Andric     PutChar(']');
483e8d8bef9SDimitry Andric   }
484e8d8bef9SDimitry Andric 
485e8d8bef9SDimitry Andric   // Curses doesn't allow direct output of color escape sequences, but that's
486e8d8bef9SDimitry Andric   // how we get source lines from the Highligher class. Read the line and
487e8d8bef9SDimitry Andric   // convert color escape sequences to curses color attributes. Use
488e8d8bef9SDimitry Andric   // first_skip_count to skip leading visible characters. Returns false if all
489e8d8bef9SDimitry Andric   // visible characters were skipped due to first_skip_count.
490e8d8bef9SDimitry Andric   bool OutputColoredStringTruncated(int right_pad, StringRef string,
491e8d8bef9SDimitry Andric                                     size_t skip_first_count,
492e8d8bef9SDimitry Andric                                     bool use_blue_background) {
493e8d8bef9SDimitry Andric     attr_t saved_attr;
494e8d8bef9SDimitry Andric     short saved_pair;
495e8d8bef9SDimitry Andric     bool result = false;
496e8d8bef9SDimitry Andric     wattr_get(m_window, &saved_attr, &saved_pair, nullptr);
497e8d8bef9SDimitry Andric     if (use_blue_background)
498e8d8bef9SDimitry Andric       ::wattron(m_window, COLOR_PAIR(WhiteOnBlue));
499e8d8bef9SDimitry Andric     while (!string.empty()) {
50081ad6265SDimitry Andric       size_t esc_pos = string.find(ANSI_ESC_START);
501e8d8bef9SDimitry Andric       if (esc_pos == StringRef::npos) {
502e8d8bef9SDimitry Andric         string = string.substr(skip_first_count);
503e8d8bef9SDimitry Andric         if (!string.empty()) {
504e8d8bef9SDimitry Andric           PutCStringTruncated(right_pad, string.data(), string.size());
505e8d8bef9SDimitry Andric           result = true;
506e8d8bef9SDimitry Andric         }
507e8d8bef9SDimitry Andric         break;
508e8d8bef9SDimitry Andric       }
509e8d8bef9SDimitry Andric       if (esc_pos > 0) {
510e8d8bef9SDimitry Andric         if (skip_first_count > 0) {
511e8d8bef9SDimitry Andric           int skip = std::min(esc_pos, skip_first_count);
512e8d8bef9SDimitry Andric           string = string.substr(skip);
513e8d8bef9SDimitry Andric           skip_first_count -= skip;
514e8d8bef9SDimitry Andric           esc_pos -= skip;
515e8d8bef9SDimitry Andric         }
516e8d8bef9SDimitry Andric         if (esc_pos > 0) {
517e8d8bef9SDimitry Andric           PutCStringTruncated(right_pad, string.data(), esc_pos);
518e8d8bef9SDimitry Andric           result = true;
519e8d8bef9SDimitry Andric           string = string.drop_front(esc_pos);
520e8d8bef9SDimitry Andric         }
521e8d8bef9SDimitry Andric       }
52281ad6265SDimitry Andric       bool consumed = string.consume_front(ANSI_ESC_START);
523e8d8bef9SDimitry Andric       assert(consumed);
524e8d8bef9SDimitry Andric       UNUSED_IF_ASSERT_DISABLED(consumed);
525e8d8bef9SDimitry Andric       // This is written to match our Highlighter classes, which seem to
526e8d8bef9SDimitry Andric       // generate only foreground color escape sequences. If necessary, this
527e8d8bef9SDimitry Andric       // will need to be extended.
52881ad6265SDimitry Andric       // Only 8 basic foreground colors, underline and reset, our Highlighter
52981ad6265SDimitry Andric       // doesn't use anything else.
530e8d8bef9SDimitry Andric       int value;
531e8d8bef9SDimitry Andric       if (!!string.consumeInteger(10, value) || // Returns false on success.
53281ad6265SDimitry Andric           !(value == 0 || value == ANSI_CTRL_UNDERLINE ||
53381ad6265SDimitry Andric             (value >= ANSI_FG_COLOR_BLACK && value <= ANSI_FG_COLOR_WHITE))) {
534e8d8bef9SDimitry Andric         llvm::errs() << "No valid color code in color escape sequence.\n";
535e8d8bef9SDimitry Andric         continue;
536e8d8bef9SDimitry Andric       }
53781ad6265SDimitry Andric       if (!string.consume_front(ANSI_ESC_END)) {
53881ad6265SDimitry Andric         llvm::errs() << "Missing '" << ANSI_ESC_END
53981ad6265SDimitry Andric                      << "' in color escape sequence.\n";
540e8d8bef9SDimitry Andric         continue;
541e8d8bef9SDimitry Andric       }
542e8d8bef9SDimitry Andric       if (value == 0) { // Reset.
543e8d8bef9SDimitry Andric         wattr_set(m_window, saved_attr, saved_pair, nullptr);
544e8d8bef9SDimitry Andric         if (use_blue_background)
545e8d8bef9SDimitry Andric           ::wattron(m_window, COLOR_PAIR(WhiteOnBlue));
54681ad6265SDimitry Andric       } else if (value == ANSI_CTRL_UNDERLINE) {
54781ad6265SDimitry Andric         ::wattron(m_window, A_UNDERLINE);
548e8d8bef9SDimitry Andric       } else {
549e8d8bef9SDimitry Andric         // Mapped directly to first 16 color pairs (black/blue background).
55081ad6265SDimitry Andric         ::wattron(m_window, COLOR_PAIR(value - ANSI_FG_COLOR_BLACK + 1 +
55181ad6265SDimitry Andric                                        (use_blue_background ? 8 : 0)));
552e8d8bef9SDimitry Andric       }
553e8d8bef9SDimitry Andric     }
554e8d8bef9SDimitry Andric     wattr_set(m_window, saved_attr, saved_pair, nullptr);
555e8d8bef9SDimitry Andric     return result;
556e8d8bef9SDimitry Andric   }
557e8d8bef9SDimitry Andric 
558fe6060f1SDimitry Andric protected:
559349cc55cSDimitry Andric   Type m_type;
56081ad6265SDimitry Andric   WINDOW *m_window = nullptr;
561fe6060f1SDimitry Andric };
562fe6060f1SDimitry Andric 
563fe6060f1SDimitry Andric class Pad : public Surface {
564fe6060f1SDimitry Andric public:
565349cc55cSDimitry Andric   Pad(Size size) : Surface(Surface::Type::Pad) {
566349cc55cSDimitry Andric     m_window = ::newpad(size.height, size.width);
567349cc55cSDimitry Andric   }
568fe6060f1SDimitry Andric 
569fe6060f1SDimitry Andric   ~Pad() { ::delwin(m_window); }
570fe6060f1SDimitry Andric };
571fe6060f1SDimitry Andric 
572fe6060f1SDimitry Andric class Window : public Surface {
573fe6060f1SDimitry Andric public:
574fe6060f1SDimitry Andric   Window(const char *name)
575349cc55cSDimitry Andric       : Surface(Surface::Type::Window), m_name(name), m_panel(nullptr),
576349cc55cSDimitry Andric         m_parent(nullptr), m_subwindows(), m_delegate_sp(),
577349cc55cSDimitry Andric         m_curr_active_window_idx(UINT32_MAX),
578fe6060f1SDimitry Andric         m_prev_active_window_idx(UINT32_MAX), m_delete(false),
579fe6060f1SDimitry Andric         m_needs_update(true), m_can_activate(true), m_is_subwin(false) {}
580fe6060f1SDimitry Andric 
581fe6060f1SDimitry Andric   Window(const char *name, WINDOW *w, bool del = true)
582349cc55cSDimitry Andric       : Surface(Surface::Type::Window), m_name(name), m_panel(nullptr),
583349cc55cSDimitry Andric         m_parent(nullptr), m_subwindows(), m_delegate_sp(),
584349cc55cSDimitry Andric         m_curr_active_window_idx(UINT32_MAX),
585fe6060f1SDimitry Andric         m_prev_active_window_idx(UINT32_MAX), m_delete(del),
586fe6060f1SDimitry Andric         m_needs_update(true), m_can_activate(true), m_is_subwin(false) {
587fe6060f1SDimitry Andric     if (w)
588fe6060f1SDimitry Andric       Reset(w);
589fe6060f1SDimitry Andric   }
590fe6060f1SDimitry Andric 
591fe6060f1SDimitry Andric   Window(const char *name, const Rect &bounds)
592bdd1243dSDimitry Andric       : Surface(Surface::Type::Window), m_name(name), m_panel(nullptr),
593bdd1243dSDimitry Andric         m_parent(nullptr), m_subwindows(), m_delegate_sp(),
594bdd1243dSDimitry Andric         m_curr_active_window_idx(UINT32_MAX),
595bdd1243dSDimitry Andric         m_prev_active_window_idx(UINT32_MAX), m_delete(false),
596fe6060f1SDimitry Andric         m_needs_update(true), m_can_activate(true), m_is_subwin(false) {
597fe6060f1SDimitry Andric     Reset(::newwin(bounds.size.height, bounds.size.width, bounds.origin.y,
598fe6060f1SDimitry Andric                    bounds.origin.y));
599fe6060f1SDimitry Andric   }
600fe6060f1SDimitry Andric 
601fe6060f1SDimitry Andric   virtual ~Window() {
602fe6060f1SDimitry Andric     RemoveSubWindows();
603fe6060f1SDimitry Andric     Reset();
604fe6060f1SDimitry Andric   }
605fe6060f1SDimitry Andric 
606fe6060f1SDimitry Andric   void Reset(WINDOW *w = nullptr, bool del = true) {
607fe6060f1SDimitry Andric     if (m_window == w)
608fe6060f1SDimitry Andric       return;
609fe6060f1SDimitry Andric 
610fe6060f1SDimitry Andric     if (m_panel) {
611fe6060f1SDimitry Andric       ::del_panel(m_panel);
612fe6060f1SDimitry Andric       m_panel = nullptr;
613fe6060f1SDimitry Andric     }
614fe6060f1SDimitry Andric     if (m_window && m_delete) {
615fe6060f1SDimitry Andric       ::delwin(m_window);
616fe6060f1SDimitry Andric       m_window = nullptr;
617fe6060f1SDimitry Andric       m_delete = false;
618fe6060f1SDimitry Andric     }
619fe6060f1SDimitry Andric     if (w) {
620fe6060f1SDimitry Andric       m_window = w;
621fe6060f1SDimitry Andric       m_panel = ::new_panel(m_window);
622fe6060f1SDimitry Andric       m_delete = del;
623fe6060f1SDimitry Andric     }
624fe6060f1SDimitry Andric   }
625fe6060f1SDimitry Andric 
626fe6060f1SDimitry Andric   // Get the rectangle in our parent window
627fe6060f1SDimitry Andric   Rect GetBounds() const { return Rect(GetParentOrigin(), GetSize()); }
628fe6060f1SDimitry Andric 
629fe6060f1SDimitry Andric   Rect GetCenteredRect(int width, int height) {
630fe6060f1SDimitry Andric     Size size = GetSize();
631fe6060f1SDimitry Andric     width = std::min(size.width, width);
632fe6060f1SDimitry Andric     height = std::min(size.height, height);
633fe6060f1SDimitry Andric     int x = (size.width - width) / 2;
634fe6060f1SDimitry Andric     int y = (size.height - height) / 2;
635fe6060f1SDimitry Andric     return Rect(Point(x, y), Size(width, height));
636fe6060f1SDimitry Andric   }
637fe6060f1SDimitry Andric 
638fe6060f1SDimitry Andric   int GetChar() { return ::wgetch(m_window); }
639fe6060f1SDimitry Andric   Point GetParentOrigin() const { return Point(GetParentX(), GetParentY()); }
640fe6060f1SDimitry Andric   int GetParentX() const { return getparx(m_window); }
641fe6060f1SDimitry Andric   int GetParentY() const { return getpary(m_window); }
642fe6060f1SDimitry Andric   void MoveWindow(int x, int y) { MoveWindow(Point(x, y)); }
643fe6060f1SDimitry Andric   void Resize(int w, int h) { ::wresize(m_window, h, w); }
644fe6060f1SDimitry Andric   void Resize(const Size &size) {
645fe6060f1SDimitry Andric     ::wresize(m_window, size.height, size.width);
646fe6060f1SDimitry Andric   }
647fe6060f1SDimitry Andric   void MoveWindow(const Point &origin) {
648fe6060f1SDimitry Andric     const bool moving_window = origin != GetParentOrigin();
649fe6060f1SDimitry Andric     if (m_is_subwin && moving_window) {
650fe6060f1SDimitry Andric       // Can't move subwindows, must delete and re-create
651fe6060f1SDimitry Andric       Size size = GetSize();
652fe6060f1SDimitry Andric       Reset(::subwin(m_parent->m_window, size.height, size.width, origin.y,
653fe6060f1SDimitry Andric                      origin.x),
654fe6060f1SDimitry Andric             true);
655fe6060f1SDimitry Andric     } else {
656fe6060f1SDimitry Andric       ::mvwin(m_window, origin.y, origin.x);
657fe6060f1SDimitry Andric     }
658fe6060f1SDimitry Andric   }
659fe6060f1SDimitry Andric 
660fe6060f1SDimitry Andric   void SetBounds(const Rect &bounds) {
661fe6060f1SDimitry Andric     const bool moving_window = bounds.origin != GetParentOrigin();
662fe6060f1SDimitry Andric     if (m_is_subwin && moving_window) {
663fe6060f1SDimitry Andric       // Can't move subwindows, must delete and re-create
664fe6060f1SDimitry Andric       Reset(::subwin(m_parent->m_window, bounds.size.height, bounds.size.width,
665fe6060f1SDimitry Andric                      bounds.origin.y, bounds.origin.x),
666fe6060f1SDimitry Andric             true);
667fe6060f1SDimitry Andric     } else {
668fe6060f1SDimitry Andric       if (moving_window)
669fe6060f1SDimitry Andric         MoveWindow(bounds.origin);
670fe6060f1SDimitry Andric       Resize(bounds.size);
671fe6060f1SDimitry Andric     }
672fe6060f1SDimitry Andric   }
673fe6060f1SDimitry Andric 
674480093f4SDimitry Andric   void Touch() {
675480093f4SDimitry Andric     ::touchwin(m_window);
676480093f4SDimitry Andric     if (m_parent)
677480093f4SDimitry Andric       m_parent->Touch();
678480093f4SDimitry Andric   }
679480093f4SDimitry Andric 
680480093f4SDimitry Andric   WindowSP CreateSubWindow(const char *name, const Rect &bounds,
681480093f4SDimitry Andric                            bool make_active) {
682480093f4SDimitry Andric     auto get_window = [this, &bounds]() {
683480093f4SDimitry Andric       return m_window
684480093f4SDimitry Andric                  ? ::subwin(m_window, bounds.size.height, bounds.size.width,
685480093f4SDimitry Andric                             bounds.origin.y, bounds.origin.x)
686480093f4SDimitry Andric                  : ::newwin(bounds.size.height, bounds.size.width,
687480093f4SDimitry Andric                             bounds.origin.y, bounds.origin.x);
688480093f4SDimitry Andric     };
689480093f4SDimitry Andric     WindowSP subwindow_sp = std::make_shared<Window>(name, get_window(), true);
690480093f4SDimitry Andric     subwindow_sp->m_is_subwin = subwindow_sp.operator bool();
691480093f4SDimitry Andric     subwindow_sp->m_parent = this;
692480093f4SDimitry Andric     if (make_active) {
693480093f4SDimitry Andric       m_prev_active_window_idx = m_curr_active_window_idx;
694480093f4SDimitry Andric       m_curr_active_window_idx = m_subwindows.size();
695480093f4SDimitry Andric     }
696480093f4SDimitry Andric     m_subwindows.push_back(subwindow_sp);
697480093f4SDimitry Andric     ::top_panel(subwindow_sp->m_panel);
698480093f4SDimitry Andric     m_needs_update = true;
699480093f4SDimitry Andric     return subwindow_sp;
700480093f4SDimitry Andric   }
701480093f4SDimitry Andric 
702480093f4SDimitry Andric   bool RemoveSubWindow(Window *window) {
703480093f4SDimitry Andric     Windows::iterator pos, end = m_subwindows.end();
704480093f4SDimitry Andric     size_t i = 0;
705480093f4SDimitry Andric     for (pos = m_subwindows.begin(); pos != end; ++pos, ++i) {
706480093f4SDimitry Andric       if ((*pos).get() == window) {
707480093f4SDimitry Andric         if (m_prev_active_window_idx == i)
708480093f4SDimitry Andric           m_prev_active_window_idx = UINT32_MAX;
709480093f4SDimitry Andric         else if (m_prev_active_window_idx != UINT32_MAX &&
710480093f4SDimitry Andric                  m_prev_active_window_idx > i)
711480093f4SDimitry Andric           --m_prev_active_window_idx;
712480093f4SDimitry Andric 
713480093f4SDimitry Andric         if (m_curr_active_window_idx == i)
714480093f4SDimitry Andric           m_curr_active_window_idx = UINT32_MAX;
715480093f4SDimitry Andric         else if (m_curr_active_window_idx != UINT32_MAX &&
716480093f4SDimitry Andric                  m_curr_active_window_idx > i)
717480093f4SDimitry Andric           --m_curr_active_window_idx;
718480093f4SDimitry Andric         window->Erase();
719480093f4SDimitry Andric         m_subwindows.erase(pos);
720480093f4SDimitry Andric         m_needs_update = true;
721480093f4SDimitry Andric         if (m_parent)
722480093f4SDimitry Andric           m_parent->Touch();
723480093f4SDimitry Andric         else
724480093f4SDimitry Andric           ::touchwin(stdscr);
725480093f4SDimitry Andric         return true;
726480093f4SDimitry Andric       }
727480093f4SDimitry Andric     }
728480093f4SDimitry Andric     return false;
729480093f4SDimitry Andric   }
730480093f4SDimitry Andric 
731480093f4SDimitry Andric   WindowSP FindSubWindow(const char *name) {
732480093f4SDimitry Andric     Windows::iterator pos, end = m_subwindows.end();
733480093f4SDimitry Andric     size_t i = 0;
734480093f4SDimitry Andric     for (pos = m_subwindows.begin(); pos != end; ++pos, ++i) {
735480093f4SDimitry Andric       if ((*pos)->m_name == name)
736480093f4SDimitry Andric         return *pos;
737480093f4SDimitry Andric     }
738480093f4SDimitry Andric     return WindowSP();
739480093f4SDimitry Andric   }
740480093f4SDimitry Andric 
741480093f4SDimitry Andric   void RemoveSubWindows() {
742480093f4SDimitry Andric     m_curr_active_window_idx = UINT32_MAX;
743480093f4SDimitry Andric     m_prev_active_window_idx = UINT32_MAX;
744480093f4SDimitry Andric     for (Windows::iterator pos = m_subwindows.begin();
745480093f4SDimitry Andric          pos != m_subwindows.end(); pos = m_subwindows.erase(pos)) {
746480093f4SDimitry Andric       (*pos)->Erase();
747480093f4SDimitry Andric     }
748480093f4SDimitry Andric     if (m_parent)
749480093f4SDimitry Andric       m_parent->Touch();
750480093f4SDimitry Andric     else
751480093f4SDimitry Andric       ::touchwin(stdscr);
752480093f4SDimitry Andric   }
753480093f4SDimitry Andric 
754480093f4SDimitry Andric   // Window drawing utilities
755480093f4SDimitry Andric   void DrawTitleBox(const char *title, const char *bottom_message = nullptr) {
756480093f4SDimitry Andric     attr_t attr = 0;
757480093f4SDimitry Andric     if (IsActive())
758e8d8bef9SDimitry Andric       attr = A_BOLD | COLOR_PAIR(BlackOnWhite);
759480093f4SDimitry Andric     else
760480093f4SDimitry Andric       attr = 0;
761480093f4SDimitry Andric     if (attr)
762480093f4SDimitry Andric       AttributeOn(attr);
763480093f4SDimitry Andric 
764480093f4SDimitry Andric     Box();
765480093f4SDimitry Andric     MoveCursor(3, 0);
766480093f4SDimitry Andric 
767480093f4SDimitry Andric     if (title && title[0]) {
768480093f4SDimitry Andric       PutChar('<');
769480093f4SDimitry Andric       PutCString(title);
770480093f4SDimitry Andric       PutChar('>');
771480093f4SDimitry Andric     }
772480093f4SDimitry Andric 
773480093f4SDimitry Andric     if (bottom_message && bottom_message[0]) {
774480093f4SDimitry Andric       int bottom_message_length = strlen(bottom_message);
775480093f4SDimitry Andric       int x = GetWidth() - 3 - (bottom_message_length + 2);
776480093f4SDimitry Andric 
777480093f4SDimitry Andric       if (x > 0) {
778480093f4SDimitry Andric         MoveCursor(x, GetHeight() - 1);
779480093f4SDimitry Andric         PutChar('[');
780480093f4SDimitry Andric         PutCString(bottom_message);
781480093f4SDimitry Andric         PutChar(']');
782480093f4SDimitry Andric       } else {
783480093f4SDimitry Andric         MoveCursor(1, GetHeight() - 1);
784480093f4SDimitry Andric         PutChar('[');
785e8d8bef9SDimitry Andric         PutCStringTruncated(1, bottom_message);
786480093f4SDimitry Andric       }
787480093f4SDimitry Andric     }
788480093f4SDimitry Andric     if (attr)
789480093f4SDimitry Andric       AttributeOff(attr);
790480093f4SDimitry Andric   }
791480093f4SDimitry Andric 
792480093f4SDimitry Andric   virtual void Draw(bool force) {
793480093f4SDimitry Andric     if (m_delegate_sp && m_delegate_sp->WindowDelegateDraw(*this, force))
794480093f4SDimitry Andric       return;
795480093f4SDimitry Andric 
796480093f4SDimitry Andric     for (auto &subwindow_sp : m_subwindows)
797480093f4SDimitry Andric       subwindow_sp->Draw(force);
798480093f4SDimitry Andric   }
799480093f4SDimitry Andric 
800480093f4SDimitry Andric   bool CreateHelpSubwindow() {
801480093f4SDimitry Andric     if (m_delegate_sp) {
802480093f4SDimitry Andric       const char *text = m_delegate_sp->WindowDelegateGetHelpText();
803480093f4SDimitry Andric       KeyHelp *key_help = m_delegate_sp->WindowDelegateGetKeyHelp();
804480093f4SDimitry Andric       if ((text && text[0]) || key_help) {
805480093f4SDimitry Andric         std::unique_ptr<HelpDialogDelegate> help_delegate_up(
806480093f4SDimitry Andric             new HelpDialogDelegate(text, key_help));
807480093f4SDimitry Andric         const size_t num_lines = help_delegate_up->GetNumLines();
808480093f4SDimitry Andric         const size_t max_length = help_delegate_up->GetMaxLineLength();
809480093f4SDimitry Andric         Rect bounds = GetBounds();
810480093f4SDimitry Andric         bounds.Inset(1, 1);
811480093f4SDimitry Andric         if (max_length + 4 < static_cast<size_t>(bounds.size.width)) {
812480093f4SDimitry Andric           bounds.origin.x += (bounds.size.width - max_length + 4) / 2;
813480093f4SDimitry Andric           bounds.size.width = max_length + 4;
814480093f4SDimitry Andric         } else {
815480093f4SDimitry Andric           if (bounds.size.width > 100) {
816480093f4SDimitry Andric             const int inset_w = bounds.size.width / 4;
817480093f4SDimitry Andric             bounds.origin.x += inset_w;
818480093f4SDimitry Andric             bounds.size.width -= 2 * inset_w;
819480093f4SDimitry Andric           }
820480093f4SDimitry Andric         }
821480093f4SDimitry Andric 
822480093f4SDimitry Andric         if (num_lines + 2 < static_cast<size_t>(bounds.size.height)) {
823480093f4SDimitry Andric           bounds.origin.y += (bounds.size.height - num_lines + 2) / 2;
824480093f4SDimitry Andric           bounds.size.height = num_lines + 2;
825480093f4SDimitry Andric         } else {
826480093f4SDimitry Andric           if (bounds.size.height > 100) {
827480093f4SDimitry Andric             const int inset_h = bounds.size.height / 4;
828480093f4SDimitry Andric             bounds.origin.y += inset_h;
829480093f4SDimitry Andric             bounds.size.height -= 2 * inset_h;
830480093f4SDimitry Andric           }
831480093f4SDimitry Andric         }
832480093f4SDimitry Andric         WindowSP help_window_sp;
833480093f4SDimitry Andric         Window *parent_window = GetParent();
834480093f4SDimitry Andric         if (parent_window)
835480093f4SDimitry Andric           help_window_sp = parent_window->CreateSubWindow("Help", bounds, true);
836480093f4SDimitry Andric         else
837480093f4SDimitry Andric           help_window_sp = CreateSubWindow("Help", bounds, true);
838480093f4SDimitry Andric         help_window_sp->SetDelegate(
839480093f4SDimitry Andric             WindowDelegateSP(help_delegate_up.release()));
840480093f4SDimitry Andric         return true;
841480093f4SDimitry Andric       }
842480093f4SDimitry Andric     }
843480093f4SDimitry Andric     return false;
844480093f4SDimitry Andric   }
845480093f4SDimitry Andric 
846480093f4SDimitry Andric   virtual HandleCharResult HandleChar(int key) {
847480093f4SDimitry Andric     // Always check the active window first
848480093f4SDimitry Andric     HandleCharResult result = eKeyNotHandled;
849480093f4SDimitry Andric     WindowSP active_window_sp = GetActiveWindow();
850480093f4SDimitry Andric     if (active_window_sp) {
851480093f4SDimitry Andric       result = active_window_sp->HandleChar(key);
852480093f4SDimitry Andric       if (result != eKeyNotHandled)
853480093f4SDimitry Andric         return result;
854480093f4SDimitry Andric     }
855480093f4SDimitry Andric 
856480093f4SDimitry Andric     if (m_delegate_sp) {
857480093f4SDimitry Andric       result = m_delegate_sp->WindowDelegateHandleChar(*this, key);
858480093f4SDimitry Andric       if (result != eKeyNotHandled)
859480093f4SDimitry Andric         return result;
860480093f4SDimitry Andric     }
861480093f4SDimitry Andric 
862480093f4SDimitry Andric     // Then check for any windows that want any keys that weren't handled. This
863480093f4SDimitry Andric     // is typically only for a menubar. Make a copy of the subwindows in case
864480093f4SDimitry Andric     // any HandleChar() functions muck with the subwindows. If we don't do
865480093f4SDimitry Andric     // this, we can crash when iterating over the subwindows.
866480093f4SDimitry Andric     Windows subwindows(m_subwindows);
867480093f4SDimitry Andric     for (auto subwindow_sp : subwindows) {
868480093f4SDimitry Andric       if (!subwindow_sp->m_can_activate) {
869480093f4SDimitry Andric         HandleCharResult result = subwindow_sp->HandleChar(key);
870480093f4SDimitry Andric         if (result != eKeyNotHandled)
871480093f4SDimitry Andric           return result;
872480093f4SDimitry Andric       }
873480093f4SDimitry Andric     }
874480093f4SDimitry Andric 
875480093f4SDimitry Andric     return eKeyNotHandled;
876480093f4SDimitry Andric   }
877480093f4SDimitry Andric 
878480093f4SDimitry Andric   WindowSP GetActiveWindow() {
879480093f4SDimitry Andric     if (!m_subwindows.empty()) {
880480093f4SDimitry Andric       if (m_curr_active_window_idx >= m_subwindows.size()) {
881480093f4SDimitry Andric         if (m_prev_active_window_idx < m_subwindows.size()) {
882480093f4SDimitry Andric           m_curr_active_window_idx = m_prev_active_window_idx;
883480093f4SDimitry Andric           m_prev_active_window_idx = UINT32_MAX;
884480093f4SDimitry Andric         } else if (IsActive()) {
885480093f4SDimitry Andric           m_prev_active_window_idx = UINT32_MAX;
886480093f4SDimitry Andric           m_curr_active_window_idx = UINT32_MAX;
887480093f4SDimitry Andric 
888480093f4SDimitry Andric           // Find first window that wants to be active if this window is active
889480093f4SDimitry Andric           const size_t num_subwindows = m_subwindows.size();
890480093f4SDimitry Andric           for (size_t i = 0; i < num_subwindows; ++i) {
891480093f4SDimitry Andric             if (m_subwindows[i]->GetCanBeActive()) {
892480093f4SDimitry Andric               m_curr_active_window_idx = i;
893480093f4SDimitry Andric               break;
894480093f4SDimitry Andric             }
895480093f4SDimitry Andric           }
896480093f4SDimitry Andric         }
897480093f4SDimitry Andric       }
898480093f4SDimitry Andric 
899480093f4SDimitry Andric       if (m_curr_active_window_idx < m_subwindows.size())
900480093f4SDimitry Andric         return m_subwindows[m_curr_active_window_idx];
901480093f4SDimitry Andric     }
902480093f4SDimitry Andric     return WindowSP();
903480093f4SDimitry Andric   }
904480093f4SDimitry Andric 
905480093f4SDimitry Andric   bool GetCanBeActive() const { return m_can_activate; }
906480093f4SDimitry Andric 
907480093f4SDimitry Andric   void SetCanBeActive(bool b) { m_can_activate = b; }
908480093f4SDimitry Andric 
909480093f4SDimitry Andric   void SetDelegate(const WindowDelegateSP &delegate_sp) {
910480093f4SDimitry Andric     m_delegate_sp = delegate_sp;
911480093f4SDimitry Andric   }
912480093f4SDimitry Andric 
913480093f4SDimitry Andric   Window *GetParent() const { return m_parent; }
914480093f4SDimitry Andric 
915480093f4SDimitry Andric   bool IsActive() const {
916480093f4SDimitry Andric     if (m_parent)
917480093f4SDimitry Andric       return m_parent->GetActiveWindow().get() == this;
918480093f4SDimitry Andric     else
919480093f4SDimitry Andric       return true; // Top level window is always active
920480093f4SDimitry Andric   }
921480093f4SDimitry Andric 
922480093f4SDimitry Andric   void SelectNextWindowAsActive() {
923480093f4SDimitry Andric     // Move active focus to next window
924e8d8bef9SDimitry Andric     const int num_subwindows = m_subwindows.size();
925e8d8bef9SDimitry Andric     int start_idx = 0;
926e8d8bef9SDimitry Andric     if (m_curr_active_window_idx != UINT32_MAX) {
927480093f4SDimitry Andric       m_prev_active_window_idx = m_curr_active_window_idx;
928e8d8bef9SDimitry Andric       start_idx = m_curr_active_window_idx + 1;
929e8d8bef9SDimitry Andric     }
930e8d8bef9SDimitry Andric     for (int idx = start_idx; idx < num_subwindows; ++idx) {
931480093f4SDimitry Andric       if (m_subwindows[idx]->GetCanBeActive()) {
932480093f4SDimitry Andric         m_curr_active_window_idx = idx;
933e8d8bef9SDimitry Andric         return;
934480093f4SDimitry Andric       }
935480093f4SDimitry Andric     }
936e8d8bef9SDimitry Andric     for (int idx = 0; idx < start_idx; ++idx) {
937480093f4SDimitry Andric       if (m_subwindows[idx]->GetCanBeActive()) {
938480093f4SDimitry Andric         m_curr_active_window_idx = idx;
939480093f4SDimitry Andric         break;
940480093f4SDimitry Andric       }
941480093f4SDimitry Andric     }
942480093f4SDimitry Andric   }
943e8d8bef9SDimitry Andric 
944e8d8bef9SDimitry Andric   void SelectPreviousWindowAsActive() {
945e8d8bef9SDimitry Andric     // Move active focus to previous window
946e8d8bef9SDimitry Andric     const int num_subwindows = m_subwindows.size();
947e8d8bef9SDimitry Andric     int start_idx = num_subwindows - 1;
948e8d8bef9SDimitry Andric     if (m_curr_active_window_idx != UINT32_MAX) {
949480093f4SDimitry Andric       m_prev_active_window_idx = m_curr_active_window_idx;
950e8d8bef9SDimitry Andric       start_idx = m_curr_active_window_idx - 1;
951e8d8bef9SDimitry Andric     }
952e8d8bef9SDimitry Andric     for (int idx = start_idx; idx >= 0; --idx) {
953e8d8bef9SDimitry Andric       if (m_subwindows[idx]->GetCanBeActive()) {
954e8d8bef9SDimitry Andric         m_curr_active_window_idx = idx;
955e8d8bef9SDimitry Andric         return;
956e8d8bef9SDimitry Andric       }
957e8d8bef9SDimitry Andric     }
958e8d8bef9SDimitry Andric     for (int idx = num_subwindows - 1; idx > start_idx; --idx) {
959480093f4SDimitry Andric       if (m_subwindows[idx]->GetCanBeActive()) {
960480093f4SDimitry Andric         m_curr_active_window_idx = idx;
961480093f4SDimitry Andric         break;
962480093f4SDimitry Andric       }
963480093f4SDimitry Andric     }
964480093f4SDimitry Andric   }
965480093f4SDimitry Andric 
966480093f4SDimitry Andric   const char *GetName() const { return m_name.c_str(); }
967480093f4SDimitry Andric 
968480093f4SDimitry Andric protected:
969480093f4SDimitry Andric   std::string m_name;
970480093f4SDimitry Andric   PANEL *m_panel;
971480093f4SDimitry Andric   Window *m_parent;
972480093f4SDimitry Andric   Windows m_subwindows;
973480093f4SDimitry Andric   WindowDelegateSP m_delegate_sp;
974480093f4SDimitry Andric   uint32_t m_curr_active_window_idx;
975480093f4SDimitry Andric   uint32_t m_prev_active_window_idx;
976480093f4SDimitry Andric   bool m_delete;
977480093f4SDimitry Andric   bool m_needs_update;
978480093f4SDimitry Andric   bool m_can_activate;
979480093f4SDimitry Andric   bool m_is_subwin;
980480093f4SDimitry Andric 
981480093f4SDimitry Andric private:
9825ffd83dbSDimitry Andric   Window(const Window &) = delete;
9835ffd83dbSDimitry Andric   const Window &operator=(const Window &) = delete;
984480093f4SDimitry Andric };
985480093f4SDimitry Andric 
986fe6060f1SDimitry Andric /////////
987fe6060f1SDimitry Andric // Forms
988fe6060f1SDimitry Andric /////////
989fe6060f1SDimitry Andric 
990fe6060f1SDimitry Andric // A scroll context defines a vertical region that needs to be visible in a
991fe6060f1SDimitry Andric // scrolling area. The region is defined by the index of the start and end lines
992fe6060f1SDimitry Andric // of the region. The start and end lines may be equal, in which case, the
993fe6060f1SDimitry Andric // region is a single line.
994fe6060f1SDimitry Andric struct ScrollContext {
995fe6060f1SDimitry Andric   int start;
996fe6060f1SDimitry Andric   int end;
997fe6060f1SDimitry Andric 
998fe6060f1SDimitry Andric   ScrollContext(int line) : start(line), end(line) {}
999fe6060f1SDimitry Andric   ScrollContext(int _start, int _end) : start(_start), end(_end) {}
1000fe6060f1SDimitry Andric 
1001fe6060f1SDimitry Andric   void Offset(int offset) {
1002fe6060f1SDimitry Andric     start += offset;
1003fe6060f1SDimitry Andric     end += offset;
1004fe6060f1SDimitry Andric   }
1005fe6060f1SDimitry Andric };
1006fe6060f1SDimitry Andric 
1007fe6060f1SDimitry Andric class FieldDelegate {
1008fe6060f1SDimitry Andric public:
1009fe6060f1SDimitry Andric   virtual ~FieldDelegate() = default;
1010fe6060f1SDimitry Andric 
1011fe6060f1SDimitry Andric   // Returns the number of lines needed to draw the field. The draw method will
1012fe6060f1SDimitry Andric   // be given a surface that have exactly this number of lines.
1013fe6060f1SDimitry Andric   virtual int FieldDelegateGetHeight() = 0;
1014fe6060f1SDimitry Andric 
1015fe6060f1SDimitry Andric   // Returns the scroll context in the local coordinates of the field. By
1016fe6060f1SDimitry Andric   // default, the scroll context spans the whole field. Bigger fields with
1017fe6060f1SDimitry Andric   // internal navigation should override this method to provide a finer context.
1018fe6060f1SDimitry Andric   // Typical override methods would first get the scroll context of the internal
1019fe6060f1SDimitry Andric   // element then add the offset of the element in the field.
1020fe6060f1SDimitry Andric   virtual ScrollContext FieldDelegateGetScrollContext() {
1021fe6060f1SDimitry Andric     return ScrollContext(0, FieldDelegateGetHeight() - 1);
1022fe6060f1SDimitry Andric   }
1023fe6060f1SDimitry Andric 
1024fe6060f1SDimitry Andric   // Draw the field in the given subpad surface. The surface have a height that
1025fe6060f1SDimitry Andric   // is equal to the height returned by FieldDelegateGetHeight(). If the field
1026fe6060f1SDimitry Andric   // is selected in the form window, then is_selected will be true.
1027349cc55cSDimitry Andric   virtual void FieldDelegateDraw(Surface &surface, bool is_selected) = 0;
1028fe6060f1SDimitry Andric 
1029fe6060f1SDimitry Andric   // Handle the key that wasn't handled by the form window or a container field.
1030fe6060f1SDimitry Andric   virtual HandleCharResult FieldDelegateHandleChar(int key) {
1031fe6060f1SDimitry Andric     return eKeyNotHandled;
1032fe6060f1SDimitry Andric   }
1033fe6060f1SDimitry Andric 
1034fe6060f1SDimitry Andric   // This is executed once the user exists the field, that is, once the user
1035fe6060f1SDimitry Andric   // navigates to the next or the previous field. This is particularly useful to
1036fe6060f1SDimitry Andric   // do in-field validation and error setting. Fields with internal navigation
1037fe6060f1SDimitry Andric   // should call this method on their fields.
10380eae32dcSDimitry Andric   virtual void FieldDelegateExitCallback() {}
1039fe6060f1SDimitry Andric 
1040fe6060f1SDimitry Andric   // Fields may have internal navigation, for instance, a List Field have
1041fe6060f1SDimitry Andric   // multiple internal elements, which needs to be navigated. To allow for this
1042fe6060f1SDimitry Andric   // mechanism, the window shouldn't handle the navigation keys all the time,
1043fe6060f1SDimitry Andric   // and instead call the key handing method of the selected field. It should
1044fe6060f1SDimitry Andric   // only handle the navigation keys when the field contains a single element or
1045fe6060f1SDimitry Andric   // have the last or first element selected depending on if the user is
1046fe6060f1SDimitry Andric   // navigating forward or backward. Additionally, once a field is selected in
1047fe6060f1SDimitry Andric   // the forward or backward direction, its first or last internal element
1048fe6060f1SDimitry Andric   // should be selected. The following methods implements those mechanisms.
1049fe6060f1SDimitry Andric 
1050fe6060f1SDimitry Andric   // Returns true if the first element in the field is selected or if the field
1051fe6060f1SDimitry Andric   // contains a single element.
1052fe6060f1SDimitry Andric   virtual bool FieldDelegateOnFirstOrOnlyElement() { return true; }
1053fe6060f1SDimitry Andric 
1054fe6060f1SDimitry Andric   // Returns true if the last element in the field is selected or if the field
1055fe6060f1SDimitry Andric   // contains a single element.
1056fe6060f1SDimitry Andric   virtual bool FieldDelegateOnLastOrOnlyElement() { return true; }
1057fe6060f1SDimitry Andric 
1058fe6060f1SDimitry Andric   // Select the first element in the field if multiple elements exists.
10590eae32dcSDimitry Andric   virtual void FieldDelegateSelectFirstElement() {}
1060fe6060f1SDimitry Andric 
1061fe6060f1SDimitry Andric   // Select the last element in the field if multiple elements exists.
10620eae32dcSDimitry Andric   virtual void FieldDelegateSelectLastElement() {}
1063fe6060f1SDimitry Andric 
1064fe6060f1SDimitry Andric   // Returns true if the field has an error, false otherwise.
1065fe6060f1SDimitry Andric   virtual bool FieldDelegateHasError() { return false; }
1066fe6060f1SDimitry Andric 
1067fe6060f1SDimitry Andric   bool FieldDelegateIsVisible() { return m_is_visible; }
1068fe6060f1SDimitry Andric 
1069fe6060f1SDimitry Andric   void FieldDelegateHide() { m_is_visible = false; }
1070fe6060f1SDimitry Andric 
1071fe6060f1SDimitry Andric   void FieldDelegateShow() { m_is_visible = true; }
1072fe6060f1SDimitry Andric 
1073fe6060f1SDimitry Andric protected:
1074fe6060f1SDimitry Andric   bool m_is_visible = true;
1075fe6060f1SDimitry Andric };
1076fe6060f1SDimitry Andric 
1077fe6060f1SDimitry Andric typedef std::unique_ptr<FieldDelegate> FieldDelegateUP;
1078fe6060f1SDimitry Andric 
1079fe6060f1SDimitry Andric class TextFieldDelegate : public FieldDelegate {
1080fe6060f1SDimitry Andric public:
1081fe6060f1SDimitry Andric   TextFieldDelegate(const char *label, const char *content, bool required)
108281ad6265SDimitry Andric       : m_label(label), m_required(required) {
1083fe6060f1SDimitry Andric     if (content)
1084fe6060f1SDimitry Andric       m_content = content;
1085fe6060f1SDimitry Andric   }
1086fe6060f1SDimitry Andric 
1087fe6060f1SDimitry Andric   // Text fields are drawn as titled boxes of a single line, with a possible
1088fe6060f1SDimitry Andric   // error messages at the end.
1089fe6060f1SDimitry Andric   //
1090fe6060f1SDimitry Andric   // __[Label]___________
1091fe6060f1SDimitry Andric   // |                  |
1092fe6060f1SDimitry Andric   // |__________________|
1093fe6060f1SDimitry Andric   // - Error message if it exists.
1094fe6060f1SDimitry Andric 
1095fe6060f1SDimitry Andric   // The text field has a height of 3 lines. 2 lines for borders and 1 line for
1096fe6060f1SDimitry Andric   // the content.
1097fe6060f1SDimitry Andric   int GetFieldHeight() { return 3; }
1098fe6060f1SDimitry Andric 
1099fe6060f1SDimitry Andric   // The text field has a full height of 3 or 4 lines. 3 lines for the actual
1100fe6060f1SDimitry Andric   // field and an optional line for an error if it exists.
1101fe6060f1SDimitry Andric   int FieldDelegateGetHeight() override {
1102fe6060f1SDimitry Andric     int height = GetFieldHeight();
1103fe6060f1SDimitry Andric     if (FieldDelegateHasError())
1104fe6060f1SDimitry Andric       height++;
1105fe6060f1SDimitry Andric     return height;
1106fe6060f1SDimitry Andric   }
1107fe6060f1SDimitry Andric 
1108fe6060f1SDimitry Andric   // Get the cursor X position in the surface coordinate.
1109fe6060f1SDimitry Andric   int GetCursorXPosition() { return m_cursor_position - m_first_visibile_char; }
1110fe6060f1SDimitry Andric 
1111fe6060f1SDimitry Andric   int GetContentLength() { return m_content.length(); }
1112fe6060f1SDimitry Andric 
1113349cc55cSDimitry Andric   void DrawContent(Surface &surface, bool is_selected) {
1114349cc55cSDimitry Andric     UpdateScrolling(surface.GetWidth());
1115349cc55cSDimitry Andric 
1116fe6060f1SDimitry Andric     surface.MoveCursor(0, 0);
1117fe6060f1SDimitry Andric     const char *text = m_content.c_str() + m_first_visibile_char;
1118fe6060f1SDimitry Andric     surface.PutCString(text, surface.GetWidth());
1119fe6060f1SDimitry Andric 
1120fe6060f1SDimitry Andric     // Highlight the cursor.
1121fe6060f1SDimitry Andric     surface.MoveCursor(GetCursorXPosition(), 0);
1122fe6060f1SDimitry Andric     if (is_selected)
1123fe6060f1SDimitry Andric       surface.AttributeOn(A_REVERSE);
1124fe6060f1SDimitry Andric     if (m_cursor_position == GetContentLength())
1125fe6060f1SDimitry Andric       // Cursor is past the last character. Highlight an empty space.
1126fe6060f1SDimitry Andric       surface.PutChar(' ');
1127fe6060f1SDimitry Andric     else
1128fe6060f1SDimitry Andric       surface.PutChar(m_content[m_cursor_position]);
1129fe6060f1SDimitry Andric     if (is_selected)
1130fe6060f1SDimitry Andric       surface.AttributeOff(A_REVERSE);
1131fe6060f1SDimitry Andric   }
1132fe6060f1SDimitry Andric 
1133349cc55cSDimitry Andric   void DrawField(Surface &surface, bool is_selected) {
1134fe6060f1SDimitry Andric     surface.TitledBox(m_label.c_str());
1135fe6060f1SDimitry Andric 
1136fe6060f1SDimitry Andric     Rect content_bounds = surface.GetFrame();
1137fe6060f1SDimitry Andric     content_bounds.Inset(1, 1);
1138349cc55cSDimitry Andric     Surface content_surface = surface.SubSurface(content_bounds);
1139fe6060f1SDimitry Andric 
1140fe6060f1SDimitry Andric     DrawContent(content_surface, is_selected);
1141fe6060f1SDimitry Andric   }
1142fe6060f1SDimitry Andric 
1143349cc55cSDimitry Andric   void DrawError(Surface &surface) {
1144fe6060f1SDimitry Andric     if (!FieldDelegateHasError())
1145fe6060f1SDimitry Andric       return;
1146fe6060f1SDimitry Andric     surface.MoveCursor(0, 0);
1147fe6060f1SDimitry Andric     surface.AttributeOn(COLOR_PAIR(RedOnBlack));
1148fe6060f1SDimitry Andric     surface.PutChar(ACS_DIAMOND);
1149fe6060f1SDimitry Andric     surface.PutChar(' ');
1150fe6060f1SDimitry Andric     surface.PutCStringTruncated(1, GetError().c_str());
1151fe6060f1SDimitry Andric     surface.AttributeOff(COLOR_PAIR(RedOnBlack));
1152fe6060f1SDimitry Andric   }
1153fe6060f1SDimitry Andric 
1154349cc55cSDimitry Andric   void FieldDelegateDraw(Surface &surface, bool is_selected) override {
1155fe6060f1SDimitry Andric     Rect frame = surface.GetFrame();
1156fe6060f1SDimitry Andric     Rect field_bounds, error_bounds;
1157fe6060f1SDimitry Andric     frame.HorizontalSplit(GetFieldHeight(), field_bounds, error_bounds);
1158349cc55cSDimitry Andric     Surface field_surface = surface.SubSurface(field_bounds);
1159349cc55cSDimitry Andric     Surface error_surface = surface.SubSurface(error_bounds);
1160fe6060f1SDimitry Andric 
1161fe6060f1SDimitry Andric     DrawField(field_surface, is_selected);
1162fe6060f1SDimitry Andric     DrawError(error_surface);
1163fe6060f1SDimitry Andric   }
1164fe6060f1SDimitry Andric 
1165349cc55cSDimitry Andric   // Get the position of the last visible character.
1166349cc55cSDimitry Andric   int GetLastVisibleCharPosition(int width) {
1167349cc55cSDimitry Andric     int position = m_first_visibile_char + width - 1;
1168349cc55cSDimitry Andric     return std::min(position, GetContentLength());
1169349cc55cSDimitry Andric   }
1170349cc55cSDimitry Andric 
1171349cc55cSDimitry Andric   void UpdateScrolling(int width) {
1172349cc55cSDimitry Andric     if (m_cursor_position < m_first_visibile_char) {
1173349cc55cSDimitry Andric       m_first_visibile_char = m_cursor_position;
1174349cc55cSDimitry Andric       return;
1175349cc55cSDimitry Andric     }
1176349cc55cSDimitry Andric 
1177349cc55cSDimitry Andric     if (m_cursor_position > GetLastVisibleCharPosition(width))
1178349cc55cSDimitry Andric       m_first_visibile_char = m_cursor_position - (width - 1);
1179349cc55cSDimitry Andric   }
1180349cc55cSDimitry Andric 
1181fe6060f1SDimitry Andric   // The cursor is allowed to move one character past the string.
1182fe6060f1SDimitry Andric   // m_cursor_position is in range [0, GetContentLength()].
1183fe6060f1SDimitry Andric   void MoveCursorRight() {
1184fe6060f1SDimitry Andric     if (m_cursor_position < GetContentLength())
1185fe6060f1SDimitry Andric       m_cursor_position++;
1186fe6060f1SDimitry Andric   }
1187fe6060f1SDimitry Andric 
1188fe6060f1SDimitry Andric   void MoveCursorLeft() {
1189fe6060f1SDimitry Andric     if (m_cursor_position > 0)
1190fe6060f1SDimitry Andric       m_cursor_position--;
1191fe6060f1SDimitry Andric   }
1192fe6060f1SDimitry Andric 
1193349cc55cSDimitry Andric   void MoveCursorToStart() { m_cursor_position = 0; }
1194349cc55cSDimitry Andric 
1195349cc55cSDimitry Andric   void MoveCursorToEnd() { m_cursor_position = GetContentLength(); }
1196fe6060f1SDimitry Andric 
1197fe6060f1SDimitry Andric   void ScrollLeft() {
1198fe6060f1SDimitry Andric     if (m_first_visibile_char > 0)
1199fe6060f1SDimitry Andric       m_first_visibile_char--;
1200fe6060f1SDimitry Andric   }
1201fe6060f1SDimitry Andric 
1202349cc55cSDimitry Andric   // Insert a character at the current cursor position and advance the cursor
1203349cc55cSDimitry Andric   // position.
1204fe6060f1SDimitry Andric   void InsertChar(char character) {
1205fe6060f1SDimitry Andric     m_content.insert(m_cursor_position, 1, character);
1206fe6060f1SDimitry Andric     m_cursor_position++;
1207349cc55cSDimitry Andric     ClearError();
1208fe6060f1SDimitry Andric   }
1209fe6060f1SDimitry Andric 
1210fe6060f1SDimitry Andric   // Remove the character before the cursor position, retreat the cursor
1211349cc55cSDimitry Andric   // position, and scroll left.
1212349cc55cSDimitry Andric   void RemovePreviousChar() {
1213fe6060f1SDimitry Andric     if (m_cursor_position == 0)
1214fe6060f1SDimitry Andric       return;
1215fe6060f1SDimitry Andric 
1216fe6060f1SDimitry Andric     m_content.erase(m_cursor_position - 1, 1);
1217fe6060f1SDimitry Andric     m_cursor_position--;
1218fe6060f1SDimitry Andric     ScrollLeft();
1219349cc55cSDimitry Andric     ClearError();
1220349cc55cSDimitry Andric   }
1221349cc55cSDimitry Andric 
1222349cc55cSDimitry Andric   // Remove the character after the cursor position.
1223349cc55cSDimitry Andric   void RemoveNextChar() {
1224349cc55cSDimitry Andric     if (m_cursor_position == GetContentLength())
1225349cc55cSDimitry Andric       return;
1226349cc55cSDimitry Andric 
1227349cc55cSDimitry Andric     m_content.erase(m_cursor_position, 1);
1228349cc55cSDimitry Andric     ClearError();
1229349cc55cSDimitry Andric   }
1230349cc55cSDimitry Andric 
1231349cc55cSDimitry Andric   // Clear characters from the current cursor position to the end.
1232349cc55cSDimitry Andric   void ClearToEnd() {
1233349cc55cSDimitry Andric     m_content.erase(m_cursor_position);
1234349cc55cSDimitry Andric     ClearError();
1235349cc55cSDimitry Andric   }
1236349cc55cSDimitry Andric 
1237349cc55cSDimitry Andric   void Clear() {
1238349cc55cSDimitry Andric     m_content.clear();
1239349cc55cSDimitry Andric     m_cursor_position = 0;
1240349cc55cSDimitry Andric     ClearError();
1241fe6060f1SDimitry Andric   }
1242fe6060f1SDimitry Andric 
1243fe6060f1SDimitry Andric   // True if the key represents a char that can be inserted in the field
1244fe6060f1SDimitry Andric   // content, false otherwise.
1245349cc55cSDimitry Andric   virtual bool IsAcceptableChar(int key) {
1246349cc55cSDimitry Andric     // The behavior of isprint is undefined when the value is not representable
1247349cc55cSDimitry Andric     // as an unsigned char. So explicitly check for non-ascii key codes.
1248349cc55cSDimitry Andric     if (key > 127)
1249349cc55cSDimitry Andric       return false;
1250349cc55cSDimitry Andric     return isprint(key);
1251349cc55cSDimitry Andric   }
1252fe6060f1SDimitry Andric 
1253fe6060f1SDimitry Andric   HandleCharResult FieldDelegateHandleChar(int key) override {
1254fe6060f1SDimitry Andric     if (IsAcceptableChar(key)) {
1255fe6060f1SDimitry Andric       ClearError();
1256fe6060f1SDimitry Andric       InsertChar((char)key);
1257fe6060f1SDimitry Andric       return eKeyHandled;
1258fe6060f1SDimitry Andric     }
1259fe6060f1SDimitry Andric 
1260fe6060f1SDimitry Andric     switch (key) {
1261349cc55cSDimitry Andric     case KEY_HOME:
1262349cc55cSDimitry Andric     case KEY_CTRL_A:
1263349cc55cSDimitry Andric       MoveCursorToStart();
1264349cc55cSDimitry Andric       return eKeyHandled;
1265349cc55cSDimitry Andric     case KEY_END:
1266349cc55cSDimitry Andric     case KEY_CTRL_E:
1267349cc55cSDimitry Andric       MoveCursorToEnd();
1268349cc55cSDimitry Andric       return eKeyHandled;
1269fe6060f1SDimitry Andric     case KEY_RIGHT:
1270349cc55cSDimitry Andric     case KEY_SF:
1271fe6060f1SDimitry Andric       MoveCursorRight();
1272fe6060f1SDimitry Andric       return eKeyHandled;
1273fe6060f1SDimitry Andric     case KEY_LEFT:
1274349cc55cSDimitry Andric     case KEY_SR:
1275fe6060f1SDimitry Andric       MoveCursorLeft();
1276fe6060f1SDimitry Andric       return eKeyHandled;
1277fe6060f1SDimitry Andric     case KEY_BACKSPACE:
1278349cc55cSDimitry Andric     case KEY_DELETE:
1279349cc55cSDimitry Andric       RemovePreviousChar();
1280349cc55cSDimitry Andric       return eKeyHandled;
1281349cc55cSDimitry Andric     case KEY_DC:
1282349cc55cSDimitry Andric       RemoveNextChar();
1283349cc55cSDimitry Andric       return eKeyHandled;
1284349cc55cSDimitry Andric     case KEY_EOL:
1285349cc55cSDimitry Andric     case KEY_CTRL_K:
1286349cc55cSDimitry Andric       ClearToEnd();
1287349cc55cSDimitry Andric       return eKeyHandled;
1288349cc55cSDimitry Andric     case KEY_DL:
1289349cc55cSDimitry Andric     case KEY_CLEAR:
1290349cc55cSDimitry Andric       Clear();
1291fe6060f1SDimitry Andric       return eKeyHandled;
1292fe6060f1SDimitry Andric     default:
1293fe6060f1SDimitry Andric       break;
1294fe6060f1SDimitry Andric     }
1295fe6060f1SDimitry Andric     return eKeyNotHandled;
1296fe6060f1SDimitry Andric   }
1297fe6060f1SDimitry Andric 
1298fe6060f1SDimitry Andric   bool FieldDelegateHasError() override { return !m_error.empty(); }
1299fe6060f1SDimitry Andric 
1300fe6060f1SDimitry Andric   void FieldDelegateExitCallback() override {
1301fe6060f1SDimitry Andric     if (!IsSpecified() && m_required)
1302fe6060f1SDimitry Andric       SetError("This field is required!");
1303fe6060f1SDimitry Andric   }
1304fe6060f1SDimitry Andric 
1305fe6060f1SDimitry Andric   bool IsSpecified() { return !m_content.empty(); }
1306fe6060f1SDimitry Andric 
1307fe6060f1SDimitry Andric   void ClearError() { m_error.clear(); }
1308fe6060f1SDimitry Andric 
1309fe6060f1SDimitry Andric   const std::string &GetError() { return m_error; }
1310fe6060f1SDimitry Andric 
1311fe6060f1SDimitry Andric   void SetError(const char *error) { m_error = error; }
1312fe6060f1SDimitry Andric 
1313fe6060f1SDimitry Andric   const std::string &GetText() { return m_content; }
1314fe6060f1SDimitry Andric 
1315349cc55cSDimitry Andric   void SetText(const char *text) {
1316349cc55cSDimitry Andric     if (text == nullptr) {
1317349cc55cSDimitry Andric       m_content.clear();
1318349cc55cSDimitry Andric       return;
1319349cc55cSDimitry Andric     }
1320349cc55cSDimitry Andric     m_content = text;
1321349cc55cSDimitry Andric   }
1322349cc55cSDimitry Andric 
1323fe6060f1SDimitry Andric protected:
1324fe6060f1SDimitry Andric   std::string m_label;
1325fe6060f1SDimitry Andric   bool m_required;
1326fe6060f1SDimitry Andric   // The position of the top left corner character of the border.
1327fe6060f1SDimitry Andric   std::string m_content;
1328fe6060f1SDimitry Andric   // The cursor position in the content string itself. Can be in the range
1329fe6060f1SDimitry Andric   // [0, GetContentLength()].
133081ad6265SDimitry Andric   int m_cursor_position = 0;
1331fe6060f1SDimitry Andric   // The index of the first visible character in the content.
133281ad6265SDimitry Andric   int m_first_visibile_char = 0;
1333fe6060f1SDimitry Andric   // Optional error message. If empty, field is considered to have no error.
1334fe6060f1SDimitry Andric   std::string m_error;
1335fe6060f1SDimitry Andric };
1336fe6060f1SDimitry Andric 
1337fe6060f1SDimitry Andric class IntegerFieldDelegate : public TextFieldDelegate {
1338fe6060f1SDimitry Andric public:
1339fe6060f1SDimitry Andric   IntegerFieldDelegate(const char *label, int content, bool required)
1340fe6060f1SDimitry Andric       : TextFieldDelegate(label, std::to_string(content).c_str(), required) {}
1341fe6060f1SDimitry Andric 
1342fe6060f1SDimitry Andric   // Only accept digits.
1343fe6060f1SDimitry Andric   bool IsAcceptableChar(int key) override { return isdigit(key); }
1344fe6060f1SDimitry Andric 
1345fe6060f1SDimitry Andric   // Returns the integer content of the field.
1346fe6060f1SDimitry Andric   int GetInteger() { return std::stoi(m_content); }
1347fe6060f1SDimitry Andric };
1348fe6060f1SDimitry Andric 
1349fe6060f1SDimitry Andric class FileFieldDelegate : public TextFieldDelegate {
1350fe6060f1SDimitry Andric public:
1351fe6060f1SDimitry Andric   FileFieldDelegate(const char *label, const char *content, bool need_to_exist,
1352fe6060f1SDimitry Andric                     bool required)
1353fe6060f1SDimitry Andric       : TextFieldDelegate(label, content, required),
1354fe6060f1SDimitry Andric         m_need_to_exist(need_to_exist) {}
1355fe6060f1SDimitry Andric 
1356fe6060f1SDimitry Andric   void FieldDelegateExitCallback() override {
1357fe6060f1SDimitry Andric     TextFieldDelegate::FieldDelegateExitCallback();
1358fe6060f1SDimitry Andric     if (!IsSpecified())
1359fe6060f1SDimitry Andric       return;
1360fe6060f1SDimitry Andric 
1361fe6060f1SDimitry Andric     if (!m_need_to_exist)
1362fe6060f1SDimitry Andric       return;
1363fe6060f1SDimitry Andric 
1364fe6060f1SDimitry Andric     FileSpec file = GetResolvedFileSpec();
1365fe6060f1SDimitry Andric     if (!FileSystem::Instance().Exists(file)) {
1366fe6060f1SDimitry Andric       SetError("File doesn't exist!");
1367fe6060f1SDimitry Andric       return;
1368fe6060f1SDimitry Andric     }
1369fe6060f1SDimitry Andric     if (FileSystem::Instance().IsDirectory(file)) {
1370fe6060f1SDimitry Andric       SetError("Not a file!");
1371fe6060f1SDimitry Andric       return;
1372fe6060f1SDimitry Andric     }
1373fe6060f1SDimitry Andric   }
1374fe6060f1SDimitry Andric 
1375fe6060f1SDimitry Andric   FileSpec GetFileSpec() {
1376fe6060f1SDimitry Andric     FileSpec file_spec(GetPath());
1377fe6060f1SDimitry Andric     return file_spec;
1378fe6060f1SDimitry Andric   }
1379fe6060f1SDimitry Andric 
1380fe6060f1SDimitry Andric   FileSpec GetResolvedFileSpec() {
1381fe6060f1SDimitry Andric     FileSpec file_spec(GetPath());
1382fe6060f1SDimitry Andric     FileSystem::Instance().Resolve(file_spec);
1383fe6060f1SDimitry Andric     return file_spec;
1384fe6060f1SDimitry Andric   }
1385fe6060f1SDimitry Andric 
1386fe6060f1SDimitry Andric   const std::string &GetPath() { return m_content; }
1387fe6060f1SDimitry Andric 
1388fe6060f1SDimitry Andric protected:
1389fe6060f1SDimitry Andric   bool m_need_to_exist;
1390fe6060f1SDimitry Andric };
1391fe6060f1SDimitry Andric 
1392fe6060f1SDimitry Andric class DirectoryFieldDelegate : public TextFieldDelegate {
1393fe6060f1SDimitry Andric public:
1394fe6060f1SDimitry Andric   DirectoryFieldDelegate(const char *label, const char *content,
1395fe6060f1SDimitry Andric                          bool need_to_exist, bool required)
1396fe6060f1SDimitry Andric       : TextFieldDelegate(label, content, required),
1397fe6060f1SDimitry Andric         m_need_to_exist(need_to_exist) {}
1398fe6060f1SDimitry Andric 
1399fe6060f1SDimitry Andric   void FieldDelegateExitCallback() override {
1400fe6060f1SDimitry Andric     TextFieldDelegate::FieldDelegateExitCallback();
1401fe6060f1SDimitry Andric     if (!IsSpecified())
1402fe6060f1SDimitry Andric       return;
1403fe6060f1SDimitry Andric 
1404fe6060f1SDimitry Andric     if (!m_need_to_exist)
1405fe6060f1SDimitry Andric       return;
1406fe6060f1SDimitry Andric 
1407fe6060f1SDimitry Andric     FileSpec file = GetResolvedFileSpec();
1408fe6060f1SDimitry Andric     if (!FileSystem::Instance().Exists(file)) {
1409fe6060f1SDimitry Andric       SetError("Directory doesn't exist!");
1410fe6060f1SDimitry Andric       return;
1411fe6060f1SDimitry Andric     }
1412fe6060f1SDimitry Andric     if (!FileSystem::Instance().IsDirectory(file)) {
1413fe6060f1SDimitry Andric       SetError("Not a directory!");
1414fe6060f1SDimitry Andric       return;
1415fe6060f1SDimitry Andric     }
1416fe6060f1SDimitry Andric   }
1417fe6060f1SDimitry Andric 
1418fe6060f1SDimitry Andric   FileSpec GetFileSpec() {
1419fe6060f1SDimitry Andric     FileSpec file_spec(GetPath());
1420fe6060f1SDimitry Andric     return file_spec;
1421fe6060f1SDimitry Andric   }
1422fe6060f1SDimitry Andric 
1423fe6060f1SDimitry Andric   FileSpec GetResolvedFileSpec() {
1424fe6060f1SDimitry Andric     FileSpec file_spec(GetPath());
1425fe6060f1SDimitry Andric     FileSystem::Instance().Resolve(file_spec);
1426fe6060f1SDimitry Andric     return file_spec;
1427fe6060f1SDimitry Andric   }
1428fe6060f1SDimitry Andric 
1429fe6060f1SDimitry Andric   const std::string &GetPath() { return m_content; }
1430fe6060f1SDimitry Andric 
1431fe6060f1SDimitry Andric protected:
1432fe6060f1SDimitry Andric   bool m_need_to_exist;
1433fe6060f1SDimitry Andric };
1434fe6060f1SDimitry Andric 
1435fe6060f1SDimitry Andric class ArchFieldDelegate : public TextFieldDelegate {
1436fe6060f1SDimitry Andric public:
1437fe6060f1SDimitry Andric   ArchFieldDelegate(const char *label, const char *content, bool required)
1438fe6060f1SDimitry Andric       : TextFieldDelegate(label, content, required) {}
1439fe6060f1SDimitry Andric 
1440fe6060f1SDimitry Andric   void FieldDelegateExitCallback() override {
1441fe6060f1SDimitry Andric     TextFieldDelegate::FieldDelegateExitCallback();
1442fe6060f1SDimitry Andric     if (!IsSpecified())
1443fe6060f1SDimitry Andric       return;
1444fe6060f1SDimitry Andric 
1445fe6060f1SDimitry Andric     if (!GetArchSpec().IsValid())
1446fe6060f1SDimitry Andric       SetError("Not a valid arch!");
1447fe6060f1SDimitry Andric   }
1448fe6060f1SDimitry Andric 
1449fe6060f1SDimitry Andric   const std::string &GetArchString() { return m_content; }
1450fe6060f1SDimitry Andric 
1451fe6060f1SDimitry Andric   ArchSpec GetArchSpec() { return ArchSpec(GetArchString()); }
1452fe6060f1SDimitry Andric };
1453fe6060f1SDimitry Andric 
1454fe6060f1SDimitry Andric class BooleanFieldDelegate : public FieldDelegate {
1455fe6060f1SDimitry Andric public:
1456fe6060f1SDimitry Andric   BooleanFieldDelegate(const char *label, bool content)
1457fe6060f1SDimitry Andric       : m_label(label), m_content(content) {}
1458fe6060f1SDimitry Andric 
1459fe6060f1SDimitry Andric   // Boolean fields are drawn as checkboxes.
1460fe6060f1SDimitry Andric   //
1461fe6060f1SDimitry Andric   // [X] Label  or [ ] Label
1462fe6060f1SDimitry Andric 
1463fe6060f1SDimitry Andric   // Boolean fields are have a single line.
1464fe6060f1SDimitry Andric   int FieldDelegateGetHeight() override { return 1; }
1465fe6060f1SDimitry Andric 
1466349cc55cSDimitry Andric   void FieldDelegateDraw(Surface &surface, bool is_selected) override {
1467fe6060f1SDimitry Andric     surface.MoveCursor(0, 0);
1468fe6060f1SDimitry Andric     surface.PutChar('[');
1469fe6060f1SDimitry Andric     if (is_selected)
1470fe6060f1SDimitry Andric       surface.AttributeOn(A_REVERSE);
1471fe6060f1SDimitry Andric     surface.PutChar(m_content ? ACS_DIAMOND : ' ');
1472fe6060f1SDimitry Andric     if (is_selected)
1473fe6060f1SDimitry Andric       surface.AttributeOff(A_REVERSE);
1474fe6060f1SDimitry Andric     surface.PutChar(']');
1475fe6060f1SDimitry Andric     surface.PutChar(' ');
1476fe6060f1SDimitry Andric     surface.PutCString(m_label.c_str());
1477fe6060f1SDimitry Andric   }
1478fe6060f1SDimitry Andric 
1479fe6060f1SDimitry Andric   void ToggleContent() { m_content = !m_content; }
1480fe6060f1SDimitry Andric 
1481fe6060f1SDimitry Andric   void SetContentToTrue() { m_content = true; }
1482fe6060f1SDimitry Andric 
1483fe6060f1SDimitry Andric   void SetContentToFalse() { m_content = false; }
1484fe6060f1SDimitry Andric 
1485fe6060f1SDimitry Andric   HandleCharResult FieldDelegateHandleChar(int key) override {
1486fe6060f1SDimitry Andric     switch (key) {
1487fe6060f1SDimitry Andric     case 't':
1488fe6060f1SDimitry Andric     case '1':
1489fe6060f1SDimitry Andric       SetContentToTrue();
1490fe6060f1SDimitry Andric       return eKeyHandled;
1491fe6060f1SDimitry Andric     case 'f':
1492fe6060f1SDimitry Andric     case '0':
1493fe6060f1SDimitry Andric       SetContentToFalse();
1494fe6060f1SDimitry Andric       return eKeyHandled;
1495fe6060f1SDimitry Andric     case ' ':
1496fe6060f1SDimitry Andric     case '\r':
1497fe6060f1SDimitry Andric     case '\n':
1498fe6060f1SDimitry Andric     case KEY_ENTER:
1499fe6060f1SDimitry Andric       ToggleContent();
1500fe6060f1SDimitry Andric       return eKeyHandled;
1501fe6060f1SDimitry Andric     default:
1502fe6060f1SDimitry Andric       break;
1503fe6060f1SDimitry Andric     }
1504fe6060f1SDimitry Andric     return eKeyNotHandled;
1505fe6060f1SDimitry Andric   }
1506fe6060f1SDimitry Andric 
1507fe6060f1SDimitry Andric   // Returns the boolean content of the field.
1508fe6060f1SDimitry Andric   bool GetBoolean() { return m_content; }
1509fe6060f1SDimitry Andric 
1510fe6060f1SDimitry Andric protected:
1511fe6060f1SDimitry Andric   std::string m_label;
1512fe6060f1SDimitry Andric   bool m_content;
1513fe6060f1SDimitry Andric };
1514fe6060f1SDimitry Andric 
1515fe6060f1SDimitry Andric class ChoicesFieldDelegate : public FieldDelegate {
1516fe6060f1SDimitry Andric public:
1517fe6060f1SDimitry Andric   ChoicesFieldDelegate(const char *label, int number_of_visible_choices,
1518fe6060f1SDimitry Andric                        std::vector<std::string> choices)
1519fe6060f1SDimitry Andric       : m_label(label), m_number_of_visible_choices(number_of_visible_choices),
152081ad6265SDimitry Andric         m_choices(choices) {}
1521fe6060f1SDimitry Andric 
1522fe6060f1SDimitry Andric   // Choices fields are drawn as titles boxses of a number of visible choices.
1523fe6060f1SDimitry Andric   // The rest of the choices become visible as the user scroll. The selected
1524fe6060f1SDimitry Andric   // choice is denoted by a diamond as the first character.
1525fe6060f1SDimitry Andric   //
1526fe6060f1SDimitry Andric   // __[Label]___________
1527fe6060f1SDimitry Andric   // |-Choice 1         |
1528fe6060f1SDimitry Andric   // | Choice 2         |
1529fe6060f1SDimitry Andric   // | Choice 3         |
1530fe6060f1SDimitry Andric   // |__________________|
1531fe6060f1SDimitry Andric 
1532fe6060f1SDimitry Andric   // Choices field have two border characters plus the number of visible
1533fe6060f1SDimitry Andric   // choices.
1534fe6060f1SDimitry Andric   int FieldDelegateGetHeight() override {
1535fe6060f1SDimitry Andric     return m_number_of_visible_choices + 2;
1536fe6060f1SDimitry Andric   }
1537fe6060f1SDimitry Andric 
1538fe6060f1SDimitry Andric   int GetNumberOfChoices() { return m_choices.size(); }
1539fe6060f1SDimitry Andric 
1540fe6060f1SDimitry Andric   // Get the index of the last visible choice.
1541fe6060f1SDimitry Andric   int GetLastVisibleChoice() {
1542fe6060f1SDimitry Andric     int index = m_first_visibile_choice + m_number_of_visible_choices;
1543fe6060f1SDimitry Andric     return std::min(index, GetNumberOfChoices()) - 1;
1544fe6060f1SDimitry Andric   }
1545fe6060f1SDimitry Andric 
1546349cc55cSDimitry Andric   void DrawContent(Surface &surface, bool is_selected) {
1547fe6060f1SDimitry Andric     int choices_to_draw = GetLastVisibleChoice() - m_first_visibile_choice + 1;
1548fe6060f1SDimitry Andric     for (int i = 0; i < choices_to_draw; i++) {
1549fe6060f1SDimitry Andric       surface.MoveCursor(0, i);
1550fe6060f1SDimitry Andric       int current_choice = m_first_visibile_choice + i;
1551fe6060f1SDimitry Andric       const char *text = m_choices[current_choice].c_str();
1552fe6060f1SDimitry Andric       bool highlight = is_selected && current_choice == m_choice;
1553fe6060f1SDimitry Andric       if (highlight)
1554fe6060f1SDimitry Andric         surface.AttributeOn(A_REVERSE);
1555fe6060f1SDimitry Andric       surface.PutChar(current_choice == m_choice ? ACS_DIAMOND : ' ');
1556fe6060f1SDimitry Andric       surface.PutCString(text);
1557fe6060f1SDimitry Andric       if (highlight)
1558fe6060f1SDimitry Andric         surface.AttributeOff(A_REVERSE);
1559fe6060f1SDimitry Andric     }
1560fe6060f1SDimitry Andric   }
1561fe6060f1SDimitry Andric 
1562349cc55cSDimitry Andric   void FieldDelegateDraw(Surface &surface, bool is_selected) override {
1563fe6060f1SDimitry Andric     UpdateScrolling();
1564fe6060f1SDimitry Andric 
1565fe6060f1SDimitry Andric     surface.TitledBox(m_label.c_str());
1566fe6060f1SDimitry Andric 
1567fe6060f1SDimitry Andric     Rect content_bounds = surface.GetFrame();
1568fe6060f1SDimitry Andric     content_bounds.Inset(1, 1);
1569349cc55cSDimitry Andric     Surface content_surface = surface.SubSurface(content_bounds);
1570fe6060f1SDimitry Andric 
1571fe6060f1SDimitry Andric     DrawContent(content_surface, is_selected);
1572fe6060f1SDimitry Andric   }
1573fe6060f1SDimitry Andric 
1574fe6060f1SDimitry Andric   void SelectPrevious() {
1575fe6060f1SDimitry Andric     if (m_choice > 0)
1576fe6060f1SDimitry Andric       m_choice--;
1577fe6060f1SDimitry Andric   }
1578fe6060f1SDimitry Andric 
1579fe6060f1SDimitry Andric   void SelectNext() {
1580fe6060f1SDimitry Andric     if (m_choice < GetNumberOfChoices() - 1)
1581fe6060f1SDimitry Andric       m_choice++;
1582fe6060f1SDimitry Andric   }
1583fe6060f1SDimitry Andric 
1584fe6060f1SDimitry Andric   void UpdateScrolling() {
1585fe6060f1SDimitry Andric     if (m_choice > GetLastVisibleChoice()) {
1586fe6060f1SDimitry Andric       m_first_visibile_choice = m_choice - (m_number_of_visible_choices - 1);
1587fe6060f1SDimitry Andric       return;
1588fe6060f1SDimitry Andric     }
1589fe6060f1SDimitry Andric 
1590fe6060f1SDimitry Andric     if (m_choice < m_first_visibile_choice)
1591fe6060f1SDimitry Andric       m_first_visibile_choice = m_choice;
1592fe6060f1SDimitry Andric   }
1593fe6060f1SDimitry Andric 
1594fe6060f1SDimitry Andric   HandleCharResult FieldDelegateHandleChar(int key) override {
1595fe6060f1SDimitry Andric     switch (key) {
1596fe6060f1SDimitry Andric     case KEY_UP:
1597fe6060f1SDimitry Andric       SelectPrevious();
1598fe6060f1SDimitry Andric       return eKeyHandled;
1599fe6060f1SDimitry Andric     case KEY_DOWN:
1600fe6060f1SDimitry Andric       SelectNext();
1601fe6060f1SDimitry Andric       return eKeyHandled;
1602fe6060f1SDimitry Andric     default:
1603fe6060f1SDimitry Andric       break;
1604fe6060f1SDimitry Andric     }
1605fe6060f1SDimitry Andric     return eKeyNotHandled;
1606fe6060f1SDimitry Andric   }
1607fe6060f1SDimitry Andric 
1608fe6060f1SDimitry Andric   // Returns the content of the choice as a string.
1609fe6060f1SDimitry Andric   std::string GetChoiceContent() { return m_choices[m_choice]; }
1610fe6060f1SDimitry Andric 
1611fe6060f1SDimitry Andric   // Returns the index of the choice.
1612fe6060f1SDimitry Andric   int GetChoice() { return m_choice; }
1613fe6060f1SDimitry Andric 
161481ad6265SDimitry Andric   void SetChoice(llvm::StringRef choice) {
1615fe6060f1SDimitry Andric     for (int i = 0; i < GetNumberOfChoices(); i++) {
1616fe6060f1SDimitry Andric       if (choice == m_choices[i]) {
1617fe6060f1SDimitry Andric         m_choice = i;
1618fe6060f1SDimitry Andric         return;
1619fe6060f1SDimitry Andric       }
1620fe6060f1SDimitry Andric     }
1621fe6060f1SDimitry Andric   }
1622fe6060f1SDimitry Andric 
1623fe6060f1SDimitry Andric protected:
1624fe6060f1SDimitry Andric   std::string m_label;
1625fe6060f1SDimitry Andric   int m_number_of_visible_choices;
1626fe6060f1SDimitry Andric   std::vector<std::string> m_choices;
1627fe6060f1SDimitry Andric   // The index of the selected choice.
162881ad6265SDimitry Andric   int m_choice = 0;
1629fe6060f1SDimitry Andric   // The index of the first visible choice in the field.
163081ad6265SDimitry Andric   int m_first_visibile_choice = 0;
1631fe6060f1SDimitry Andric };
1632fe6060f1SDimitry Andric 
1633fe6060f1SDimitry Andric class PlatformPluginFieldDelegate : public ChoicesFieldDelegate {
1634fe6060f1SDimitry Andric public:
1635fe6060f1SDimitry Andric   PlatformPluginFieldDelegate(Debugger &debugger)
1636fe6060f1SDimitry Andric       : ChoicesFieldDelegate("Platform Plugin", 3, GetPossiblePluginNames()) {
1637fe6060f1SDimitry Andric     PlatformSP platform_sp = debugger.GetPlatformList().GetSelectedPlatform();
1638fe6060f1SDimitry Andric     if (platform_sp)
163981ad6265SDimitry Andric       SetChoice(platform_sp->GetPluginName());
1640fe6060f1SDimitry Andric   }
1641fe6060f1SDimitry Andric 
1642fe6060f1SDimitry Andric   std::vector<std::string> GetPossiblePluginNames() {
1643fe6060f1SDimitry Andric     std::vector<std::string> names;
1644fe6060f1SDimitry Andric     size_t i = 0;
1645349cc55cSDimitry Andric     for (llvm::StringRef name =
1646349cc55cSDimitry Andric              PluginManager::GetPlatformPluginNameAtIndex(i++);
1647349cc55cSDimitry Andric          !name.empty(); name = PluginManager::GetProcessPluginNameAtIndex(i++))
1648349cc55cSDimitry Andric       names.push_back(name.str());
1649fe6060f1SDimitry Andric     return names;
1650fe6060f1SDimitry Andric   }
1651fe6060f1SDimitry Andric 
1652fe6060f1SDimitry Andric   std::string GetPluginName() {
1653fe6060f1SDimitry Andric     std::string plugin_name = GetChoiceContent();
1654fe6060f1SDimitry Andric     return plugin_name;
1655fe6060f1SDimitry Andric   }
1656fe6060f1SDimitry Andric };
1657fe6060f1SDimitry Andric 
1658fe6060f1SDimitry Andric class ProcessPluginFieldDelegate : public ChoicesFieldDelegate {
1659fe6060f1SDimitry Andric public:
1660fe6060f1SDimitry Andric   ProcessPluginFieldDelegate()
1661fe6060f1SDimitry Andric       : ChoicesFieldDelegate("Process Plugin", 3, GetPossiblePluginNames()) {}
1662fe6060f1SDimitry Andric 
1663fe6060f1SDimitry Andric   std::vector<std::string> GetPossiblePluginNames() {
1664fe6060f1SDimitry Andric     std::vector<std::string> names;
1665fe6060f1SDimitry Andric     names.push_back("<default>");
1666fe6060f1SDimitry Andric 
1667fe6060f1SDimitry Andric     size_t i = 0;
1668349cc55cSDimitry Andric     for (llvm::StringRef name = PluginManager::GetProcessPluginNameAtIndex(i++);
1669349cc55cSDimitry Andric          !name.empty(); name = PluginManager::GetProcessPluginNameAtIndex(i++))
1670349cc55cSDimitry Andric       names.push_back(name.str());
1671fe6060f1SDimitry Andric     return names;
1672fe6060f1SDimitry Andric   }
1673fe6060f1SDimitry Andric 
1674fe6060f1SDimitry Andric   std::string GetPluginName() {
1675fe6060f1SDimitry Andric     std::string plugin_name = GetChoiceContent();
1676fe6060f1SDimitry Andric     if (plugin_name == "<default>")
1677fe6060f1SDimitry Andric       return "";
1678fe6060f1SDimitry Andric     return plugin_name;
1679fe6060f1SDimitry Andric   }
1680fe6060f1SDimitry Andric };
1681fe6060f1SDimitry Andric 
1682349cc55cSDimitry Andric class LazyBooleanFieldDelegate : public ChoicesFieldDelegate {
1683349cc55cSDimitry Andric public:
1684349cc55cSDimitry Andric   LazyBooleanFieldDelegate(const char *label, const char *calculate_label)
1685349cc55cSDimitry Andric       : ChoicesFieldDelegate(label, 3, GetPossibleOptions(calculate_label)) {}
1686349cc55cSDimitry Andric 
1687349cc55cSDimitry Andric   static constexpr const char *kNo = "No";
1688349cc55cSDimitry Andric   static constexpr const char *kYes = "Yes";
1689349cc55cSDimitry Andric 
1690349cc55cSDimitry Andric   std::vector<std::string> GetPossibleOptions(const char *calculate_label) {
1691349cc55cSDimitry Andric     std::vector<std::string> options;
1692349cc55cSDimitry Andric     options.push_back(calculate_label);
1693349cc55cSDimitry Andric     options.push_back(kYes);
1694349cc55cSDimitry Andric     options.push_back(kNo);
1695349cc55cSDimitry Andric     return options;
1696349cc55cSDimitry Andric   }
1697349cc55cSDimitry Andric 
1698349cc55cSDimitry Andric   LazyBool GetLazyBoolean() {
1699349cc55cSDimitry Andric     std::string choice = GetChoiceContent();
1700349cc55cSDimitry Andric     if (choice == kNo)
1701349cc55cSDimitry Andric       return eLazyBoolNo;
1702349cc55cSDimitry Andric     else if (choice == kYes)
1703349cc55cSDimitry Andric       return eLazyBoolYes;
1704349cc55cSDimitry Andric     else
1705349cc55cSDimitry Andric       return eLazyBoolCalculate;
1706349cc55cSDimitry Andric   }
1707349cc55cSDimitry Andric };
1708349cc55cSDimitry Andric 
1709fe6060f1SDimitry Andric template <class T> class ListFieldDelegate : public FieldDelegate {
1710fe6060f1SDimitry Andric public:
1711fe6060f1SDimitry Andric   ListFieldDelegate(const char *label, T default_field)
171281ad6265SDimitry Andric       : m_label(label), m_default_field(default_field),
1713fe6060f1SDimitry Andric         m_selection_type(SelectionType::NewButton) {}
1714fe6060f1SDimitry Andric 
1715fe6060f1SDimitry Andric   // Signify which element is selected. If a field or a remove button is
1716fe6060f1SDimitry Andric   // selected, then m_selection_index signifies the particular field that
1717fe6060f1SDimitry Andric   // is selected or the field that the remove button belongs to.
1718fe6060f1SDimitry Andric   enum class SelectionType { Field, RemoveButton, NewButton };
1719fe6060f1SDimitry Andric 
1720fe6060f1SDimitry Andric   // A List field is drawn as a titled box of a number of other fields of the
1721fe6060f1SDimitry Andric   // same type. Each field has a Remove button next to it that removes the
1722fe6060f1SDimitry Andric   // corresponding field. Finally, the last line contains a New button to add a
1723fe6060f1SDimitry Andric   // new field.
1724fe6060f1SDimitry Andric   //
1725fe6060f1SDimitry Andric   // __[Label]___________
1726fe6060f1SDimitry Andric   // | Field 0 [Remove] |
1727fe6060f1SDimitry Andric   // | Field 1 [Remove] |
1728fe6060f1SDimitry Andric   // | Field 2 [Remove] |
1729fe6060f1SDimitry Andric   // |      [New]       |
1730fe6060f1SDimitry Andric   // |__________________|
1731fe6060f1SDimitry Andric 
1732fe6060f1SDimitry Andric   // List fields have two lines for border characters, 1 line for the New
1733fe6060f1SDimitry Andric   // button, and the total height of the available fields.
1734fe6060f1SDimitry Andric   int FieldDelegateGetHeight() override {
1735fe6060f1SDimitry Andric     // 2 border characters.
1736fe6060f1SDimitry Andric     int height = 2;
1737fe6060f1SDimitry Andric     // Total height of the fields.
1738fe6060f1SDimitry Andric     for (int i = 0; i < GetNumberOfFields(); i++) {
1739fe6060f1SDimitry Andric       height += m_fields[i].FieldDelegateGetHeight();
1740fe6060f1SDimitry Andric     }
1741fe6060f1SDimitry Andric     // A line for the New button.
1742fe6060f1SDimitry Andric     height++;
1743fe6060f1SDimitry Andric     return height;
1744fe6060f1SDimitry Andric   }
1745fe6060f1SDimitry Andric 
1746fe6060f1SDimitry Andric   ScrollContext FieldDelegateGetScrollContext() override {
1747fe6060f1SDimitry Andric     int height = FieldDelegateGetHeight();
1748fe6060f1SDimitry Andric     if (m_selection_type == SelectionType::NewButton)
1749fe6060f1SDimitry Andric       return ScrollContext(height - 2, height - 1);
1750fe6060f1SDimitry Andric 
1751fe6060f1SDimitry Andric     FieldDelegate &field = m_fields[m_selection_index];
1752fe6060f1SDimitry Andric     ScrollContext context = field.FieldDelegateGetScrollContext();
1753fe6060f1SDimitry Andric 
1754fe6060f1SDimitry Andric     // Start at 1 because of the top border.
1755fe6060f1SDimitry Andric     int offset = 1;
1756fe6060f1SDimitry Andric     for (int i = 0; i < m_selection_index; i++) {
1757fe6060f1SDimitry Andric       offset += m_fields[i].FieldDelegateGetHeight();
1758fe6060f1SDimitry Andric     }
1759fe6060f1SDimitry Andric     context.Offset(offset);
1760fe6060f1SDimitry Andric 
1761fe6060f1SDimitry Andric     // If the scroll context is touching the top border, include it in the
1762fe6060f1SDimitry Andric     // context to show the label.
1763fe6060f1SDimitry Andric     if (context.start == 1)
1764fe6060f1SDimitry Andric       context.start--;
1765fe6060f1SDimitry Andric 
1766fe6060f1SDimitry Andric     // If the scroll context is touching the new button, include it as well as
1767fe6060f1SDimitry Andric     // the bottom border in the context.
1768fe6060f1SDimitry Andric     if (context.end == height - 3)
1769fe6060f1SDimitry Andric       context.end += 2;
1770fe6060f1SDimitry Andric 
1771fe6060f1SDimitry Andric     return context;
1772fe6060f1SDimitry Andric   }
1773fe6060f1SDimitry Andric 
1774349cc55cSDimitry Andric   void DrawRemoveButton(Surface &surface, int highlight) {
1775fe6060f1SDimitry Andric     surface.MoveCursor(1, surface.GetHeight() / 2);
1776fe6060f1SDimitry Andric     if (highlight)
1777fe6060f1SDimitry Andric       surface.AttributeOn(A_REVERSE);
1778fe6060f1SDimitry Andric     surface.PutCString("[Remove]");
1779fe6060f1SDimitry Andric     if (highlight)
1780fe6060f1SDimitry Andric       surface.AttributeOff(A_REVERSE);
1781fe6060f1SDimitry Andric   }
1782fe6060f1SDimitry Andric 
1783349cc55cSDimitry Andric   void DrawFields(Surface &surface, bool is_selected) {
1784fe6060f1SDimitry Andric     int line = 0;
1785fe6060f1SDimitry Andric     int width = surface.GetWidth();
1786fe6060f1SDimitry Andric     for (int i = 0; i < GetNumberOfFields(); i++) {
1787fe6060f1SDimitry Andric       int height = m_fields[i].FieldDelegateGetHeight();
1788fe6060f1SDimitry Andric       Rect bounds = Rect(Point(0, line), Size(width, height));
1789fe6060f1SDimitry Andric       Rect field_bounds, remove_button_bounds;
1790fe6060f1SDimitry Andric       bounds.VerticalSplit(bounds.size.width - sizeof(" [Remove]"),
1791fe6060f1SDimitry Andric                            field_bounds, remove_button_bounds);
1792349cc55cSDimitry Andric       Surface field_surface = surface.SubSurface(field_bounds);
1793349cc55cSDimitry Andric       Surface remove_button_surface = surface.SubSurface(remove_button_bounds);
1794fe6060f1SDimitry Andric 
1795fe6060f1SDimitry Andric       bool is_element_selected = m_selection_index == i && is_selected;
1796fe6060f1SDimitry Andric       bool is_field_selected =
1797fe6060f1SDimitry Andric           is_element_selected && m_selection_type == SelectionType::Field;
1798fe6060f1SDimitry Andric       bool is_remove_button_selected =
1799fe6060f1SDimitry Andric           is_element_selected &&
1800fe6060f1SDimitry Andric           m_selection_type == SelectionType::RemoveButton;
1801fe6060f1SDimitry Andric       m_fields[i].FieldDelegateDraw(field_surface, is_field_selected);
1802fe6060f1SDimitry Andric       DrawRemoveButton(remove_button_surface, is_remove_button_selected);
1803fe6060f1SDimitry Andric 
1804fe6060f1SDimitry Andric       line += height;
1805fe6060f1SDimitry Andric     }
1806fe6060f1SDimitry Andric   }
1807fe6060f1SDimitry Andric 
1808349cc55cSDimitry Andric   void DrawNewButton(Surface &surface, bool is_selected) {
1809fe6060f1SDimitry Andric     const char *button_text = "[New]";
1810fe6060f1SDimitry Andric     int x = (surface.GetWidth() - sizeof(button_text) - 1) / 2;
1811fe6060f1SDimitry Andric     surface.MoveCursor(x, 0);
1812fe6060f1SDimitry Andric     bool highlight =
1813fe6060f1SDimitry Andric         is_selected && m_selection_type == SelectionType::NewButton;
1814fe6060f1SDimitry Andric     if (highlight)
1815fe6060f1SDimitry Andric       surface.AttributeOn(A_REVERSE);
1816fe6060f1SDimitry Andric     surface.PutCString(button_text);
1817fe6060f1SDimitry Andric     if (highlight)
1818fe6060f1SDimitry Andric       surface.AttributeOff(A_REVERSE);
1819fe6060f1SDimitry Andric   }
1820fe6060f1SDimitry Andric 
1821349cc55cSDimitry Andric   void FieldDelegateDraw(Surface &surface, bool is_selected) override {
1822fe6060f1SDimitry Andric     surface.TitledBox(m_label.c_str());
1823fe6060f1SDimitry Andric 
1824fe6060f1SDimitry Andric     Rect content_bounds = surface.GetFrame();
1825fe6060f1SDimitry Andric     content_bounds.Inset(1, 1);
1826fe6060f1SDimitry Andric     Rect fields_bounds, new_button_bounds;
1827fe6060f1SDimitry Andric     content_bounds.HorizontalSplit(content_bounds.size.height - 1,
1828fe6060f1SDimitry Andric                                    fields_bounds, new_button_bounds);
1829349cc55cSDimitry Andric     Surface fields_surface = surface.SubSurface(fields_bounds);
1830349cc55cSDimitry Andric     Surface new_button_surface = surface.SubSurface(new_button_bounds);
1831fe6060f1SDimitry Andric 
1832fe6060f1SDimitry Andric     DrawFields(fields_surface, is_selected);
1833fe6060f1SDimitry Andric     DrawNewButton(new_button_surface, is_selected);
1834fe6060f1SDimitry Andric   }
1835fe6060f1SDimitry Andric 
1836fe6060f1SDimitry Andric   void AddNewField() {
1837fe6060f1SDimitry Andric     m_fields.push_back(m_default_field);
1838fe6060f1SDimitry Andric     m_selection_index = GetNumberOfFields() - 1;
1839fe6060f1SDimitry Andric     m_selection_type = SelectionType::Field;
1840fe6060f1SDimitry Andric     FieldDelegate &field = m_fields[m_selection_index];
1841fe6060f1SDimitry Andric     field.FieldDelegateSelectFirstElement();
1842fe6060f1SDimitry Andric   }
1843fe6060f1SDimitry Andric 
1844fe6060f1SDimitry Andric   void RemoveField() {
1845fe6060f1SDimitry Andric     m_fields.erase(m_fields.begin() + m_selection_index);
1846fe6060f1SDimitry Andric     if (m_selection_index != 0)
1847fe6060f1SDimitry Andric       m_selection_index--;
1848fe6060f1SDimitry Andric 
1849fe6060f1SDimitry Andric     if (GetNumberOfFields() > 0) {
1850fe6060f1SDimitry Andric       m_selection_type = SelectionType::Field;
1851fe6060f1SDimitry Andric       FieldDelegate &field = m_fields[m_selection_index];
1852fe6060f1SDimitry Andric       field.FieldDelegateSelectFirstElement();
1853fe6060f1SDimitry Andric     } else
1854fe6060f1SDimitry Andric       m_selection_type = SelectionType::NewButton;
1855fe6060f1SDimitry Andric   }
1856fe6060f1SDimitry Andric 
1857fe6060f1SDimitry Andric   HandleCharResult SelectNext(int key) {
1858fe6060f1SDimitry Andric     if (m_selection_type == SelectionType::NewButton)
1859fe6060f1SDimitry Andric       return eKeyNotHandled;
1860fe6060f1SDimitry Andric 
1861fe6060f1SDimitry Andric     if (m_selection_type == SelectionType::RemoveButton) {
1862fe6060f1SDimitry Andric       if (m_selection_index == GetNumberOfFields() - 1) {
1863fe6060f1SDimitry Andric         m_selection_type = SelectionType::NewButton;
1864fe6060f1SDimitry Andric         return eKeyHandled;
1865fe6060f1SDimitry Andric       }
1866fe6060f1SDimitry Andric       m_selection_index++;
1867fe6060f1SDimitry Andric       m_selection_type = SelectionType::Field;
1868fe6060f1SDimitry Andric       FieldDelegate &next_field = m_fields[m_selection_index];
1869fe6060f1SDimitry Andric       next_field.FieldDelegateSelectFirstElement();
1870fe6060f1SDimitry Andric       return eKeyHandled;
1871fe6060f1SDimitry Andric     }
1872fe6060f1SDimitry Andric 
1873fe6060f1SDimitry Andric     FieldDelegate &field = m_fields[m_selection_index];
1874fe6060f1SDimitry Andric     if (!field.FieldDelegateOnLastOrOnlyElement()) {
1875fe6060f1SDimitry Andric       return field.FieldDelegateHandleChar(key);
1876fe6060f1SDimitry Andric     }
1877fe6060f1SDimitry Andric 
1878fe6060f1SDimitry Andric     field.FieldDelegateExitCallback();
1879fe6060f1SDimitry Andric 
1880fe6060f1SDimitry Andric     m_selection_type = SelectionType::RemoveButton;
1881fe6060f1SDimitry Andric     return eKeyHandled;
1882fe6060f1SDimitry Andric   }
1883fe6060f1SDimitry Andric 
1884fe6060f1SDimitry Andric   HandleCharResult SelectPrevious(int key) {
1885fe6060f1SDimitry Andric     if (FieldDelegateOnFirstOrOnlyElement())
1886fe6060f1SDimitry Andric       return eKeyNotHandled;
1887fe6060f1SDimitry Andric 
1888fe6060f1SDimitry Andric     if (m_selection_type == SelectionType::RemoveButton) {
1889fe6060f1SDimitry Andric       m_selection_type = SelectionType::Field;
1890fe6060f1SDimitry Andric       FieldDelegate &field = m_fields[m_selection_index];
1891fe6060f1SDimitry Andric       field.FieldDelegateSelectLastElement();
1892fe6060f1SDimitry Andric       return eKeyHandled;
1893fe6060f1SDimitry Andric     }
1894fe6060f1SDimitry Andric 
1895fe6060f1SDimitry Andric     if (m_selection_type == SelectionType::NewButton) {
1896fe6060f1SDimitry Andric       m_selection_type = SelectionType::RemoveButton;
1897fe6060f1SDimitry Andric       m_selection_index = GetNumberOfFields() - 1;
1898fe6060f1SDimitry Andric       return eKeyHandled;
1899fe6060f1SDimitry Andric     }
1900fe6060f1SDimitry Andric 
1901fe6060f1SDimitry Andric     FieldDelegate &field = m_fields[m_selection_index];
1902fe6060f1SDimitry Andric     if (!field.FieldDelegateOnFirstOrOnlyElement()) {
1903fe6060f1SDimitry Andric       return field.FieldDelegateHandleChar(key);
1904fe6060f1SDimitry Andric     }
1905fe6060f1SDimitry Andric 
1906fe6060f1SDimitry Andric     field.FieldDelegateExitCallback();
1907fe6060f1SDimitry Andric 
1908fe6060f1SDimitry Andric     m_selection_type = SelectionType::RemoveButton;
1909fe6060f1SDimitry Andric     m_selection_index--;
1910fe6060f1SDimitry Andric     return eKeyHandled;
1911fe6060f1SDimitry Andric   }
1912fe6060f1SDimitry Andric 
1913349cc55cSDimitry Andric   // If the last element of the field is selected and it didn't handle the key.
1914349cc55cSDimitry Andric   // Select the next field or new button if the selected field is the last one.
1915349cc55cSDimitry Andric   HandleCharResult SelectNextInList(int key) {
1916349cc55cSDimitry Andric     assert(m_selection_type == SelectionType::Field);
1917349cc55cSDimitry Andric 
1918349cc55cSDimitry Andric     FieldDelegate &field = m_fields[m_selection_index];
1919349cc55cSDimitry Andric     if (field.FieldDelegateHandleChar(key) == eKeyHandled)
1920349cc55cSDimitry Andric       return eKeyHandled;
1921349cc55cSDimitry Andric 
1922349cc55cSDimitry Andric     if (!field.FieldDelegateOnLastOrOnlyElement())
1923349cc55cSDimitry Andric       return eKeyNotHandled;
1924349cc55cSDimitry Andric 
1925349cc55cSDimitry Andric     field.FieldDelegateExitCallback();
1926349cc55cSDimitry Andric 
1927349cc55cSDimitry Andric     if (m_selection_index == GetNumberOfFields() - 1) {
1928349cc55cSDimitry Andric       m_selection_type = SelectionType::NewButton;
1929349cc55cSDimitry Andric       return eKeyHandled;
1930349cc55cSDimitry Andric     }
1931349cc55cSDimitry Andric 
1932349cc55cSDimitry Andric     m_selection_index++;
1933349cc55cSDimitry Andric     FieldDelegate &next_field = m_fields[m_selection_index];
1934349cc55cSDimitry Andric     next_field.FieldDelegateSelectFirstElement();
1935349cc55cSDimitry Andric     return eKeyHandled;
1936349cc55cSDimitry Andric   }
1937349cc55cSDimitry Andric 
1938fe6060f1SDimitry Andric   HandleCharResult FieldDelegateHandleChar(int key) override {
1939fe6060f1SDimitry Andric     switch (key) {
1940fe6060f1SDimitry Andric     case '\r':
1941fe6060f1SDimitry Andric     case '\n':
1942fe6060f1SDimitry Andric     case KEY_ENTER:
1943fe6060f1SDimitry Andric       switch (m_selection_type) {
1944fe6060f1SDimitry Andric       case SelectionType::NewButton:
1945fe6060f1SDimitry Andric         AddNewField();
1946fe6060f1SDimitry Andric         return eKeyHandled;
1947fe6060f1SDimitry Andric       case SelectionType::RemoveButton:
1948fe6060f1SDimitry Andric         RemoveField();
1949fe6060f1SDimitry Andric         return eKeyHandled;
1950349cc55cSDimitry Andric       case SelectionType::Field:
1951349cc55cSDimitry Andric         return SelectNextInList(key);
1952fe6060f1SDimitry Andric       }
1953fe6060f1SDimitry Andric       break;
1954fe6060f1SDimitry Andric     case '\t':
1955349cc55cSDimitry Andric       return SelectNext(key);
1956fe6060f1SDimitry Andric     case KEY_SHIFT_TAB:
1957349cc55cSDimitry Andric       return SelectPrevious(key);
1958fe6060f1SDimitry Andric     default:
1959fe6060f1SDimitry Andric       break;
1960fe6060f1SDimitry Andric     }
1961fe6060f1SDimitry Andric 
1962fe6060f1SDimitry Andric     // If the key wasn't handled and one of the fields is selected, pass the key
1963fe6060f1SDimitry Andric     // to that field.
1964fe6060f1SDimitry Andric     if (m_selection_type == SelectionType::Field) {
1965fe6060f1SDimitry Andric       return m_fields[m_selection_index].FieldDelegateHandleChar(key);
1966fe6060f1SDimitry Andric     }
1967fe6060f1SDimitry Andric 
1968fe6060f1SDimitry Andric     return eKeyNotHandled;
1969fe6060f1SDimitry Andric   }
1970fe6060f1SDimitry Andric 
1971fe6060f1SDimitry Andric   bool FieldDelegateOnLastOrOnlyElement() override {
1972fe6060f1SDimitry Andric     if (m_selection_type == SelectionType::NewButton) {
1973fe6060f1SDimitry Andric       return true;
1974fe6060f1SDimitry Andric     }
1975fe6060f1SDimitry Andric     return false;
1976fe6060f1SDimitry Andric   }
1977fe6060f1SDimitry Andric 
1978fe6060f1SDimitry Andric   bool FieldDelegateOnFirstOrOnlyElement() override {
1979fe6060f1SDimitry Andric     if (m_selection_type == SelectionType::NewButton &&
1980fe6060f1SDimitry Andric         GetNumberOfFields() == 0)
1981fe6060f1SDimitry Andric       return true;
1982fe6060f1SDimitry Andric 
1983fe6060f1SDimitry Andric     if (m_selection_type == SelectionType::Field && m_selection_index == 0) {
1984fe6060f1SDimitry Andric       FieldDelegate &field = m_fields[m_selection_index];
1985fe6060f1SDimitry Andric       return field.FieldDelegateOnFirstOrOnlyElement();
1986fe6060f1SDimitry Andric     }
1987fe6060f1SDimitry Andric 
1988fe6060f1SDimitry Andric     return false;
1989fe6060f1SDimitry Andric   }
1990fe6060f1SDimitry Andric 
1991fe6060f1SDimitry Andric   void FieldDelegateSelectFirstElement() override {
1992fe6060f1SDimitry Andric     if (GetNumberOfFields() == 0) {
1993fe6060f1SDimitry Andric       m_selection_type = SelectionType::NewButton;
1994fe6060f1SDimitry Andric       return;
1995fe6060f1SDimitry Andric     }
1996fe6060f1SDimitry Andric 
1997fe6060f1SDimitry Andric     m_selection_type = SelectionType::Field;
1998fe6060f1SDimitry Andric     m_selection_index = 0;
1999fe6060f1SDimitry Andric   }
2000fe6060f1SDimitry Andric 
2001fe6060f1SDimitry Andric   void FieldDelegateSelectLastElement() override {
2002fe6060f1SDimitry Andric     m_selection_type = SelectionType::NewButton;
2003fe6060f1SDimitry Andric   }
2004fe6060f1SDimitry Andric 
2005fe6060f1SDimitry Andric   int GetNumberOfFields() { return m_fields.size(); }
2006fe6060f1SDimitry Andric 
2007fe6060f1SDimitry Andric   // Returns the form delegate at the current index.
2008fe6060f1SDimitry Andric   T &GetField(int index) { return m_fields[index]; }
2009fe6060f1SDimitry Andric 
2010fe6060f1SDimitry Andric protected:
2011fe6060f1SDimitry Andric   std::string m_label;
2012fe6060f1SDimitry Andric   // The default field delegate instance from which new field delegates will be
2013fe6060f1SDimitry Andric   // created though a copy.
2014fe6060f1SDimitry Andric   T m_default_field;
2015fe6060f1SDimitry Andric   std::vector<T> m_fields;
201681ad6265SDimitry Andric   int m_selection_index = 0;
2017fe6060f1SDimitry Andric   // See SelectionType class enum.
2018fe6060f1SDimitry Andric   SelectionType m_selection_type;
2019fe6060f1SDimitry Andric };
2020fe6060f1SDimitry Andric 
2021349cc55cSDimitry Andric class ArgumentsFieldDelegate : public ListFieldDelegate<TextFieldDelegate> {
2022349cc55cSDimitry Andric public:
2023349cc55cSDimitry Andric   ArgumentsFieldDelegate()
2024349cc55cSDimitry Andric       : ListFieldDelegate("Arguments",
2025349cc55cSDimitry Andric                           TextFieldDelegate("Argument", "", false)) {}
2026349cc55cSDimitry Andric 
2027349cc55cSDimitry Andric   Args GetArguments() {
2028349cc55cSDimitry Andric     Args arguments;
2029349cc55cSDimitry Andric     for (int i = 0; i < GetNumberOfFields(); i++) {
2030349cc55cSDimitry Andric       arguments.AppendArgument(GetField(i).GetText());
2031349cc55cSDimitry Andric     }
2032349cc55cSDimitry Andric     return arguments;
2033349cc55cSDimitry Andric   }
2034349cc55cSDimitry Andric 
2035349cc55cSDimitry Andric   void AddArguments(const Args &arguments) {
2036349cc55cSDimitry Andric     for (size_t i = 0; i < arguments.GetArgumentCount(); i++) {
2037349cc55cSDimitry Andric       AddNewField();
2038349cc55cSDimitry Andric       TextFieldDelegate &field = GetField(GetNumberOfFields() - 1);
2039349cc55cSDimitry Andric       field.SetText(arguments.GetArgumentAtIndex(i));
2040349cc55cSDimitry Andric     }
2041349cc55cSDimitry Andric   }
2042349cc55cSDimitry Andric };
2043349cc55cSDimitry Andric 
2044349cc55cSDimitry Andric template <class KeyFieldDelegateType, class ValueFieldDelegateType>
2045349cc55cSDimitry Andric class MappingFieldDelegate : public FieldDelegate {
2046349cc55cSDimitry Andric public:
2047349cc55cSDimitry Andric   MappingFieldDelegate(KeyFieldDelegateType key_field,
2048349cc55cSDimitry Andric                        ValueFieldDelegateType value_field)
2049349cc55cSDimitry Andric       : m_key_field(key_field), m_value_field(value_field),
2050349cc55cSDimitry Andric         m_selection_type(SelectionType::Key) {}
2051349cc55cSDimitry Andric 
2052349cc55cSDimitry Andric   // Signify which element is selected. The key field or its value field.
2053349cc55cSDimitry Andric   enum class SelectionType { Key, Value };
2054349cc55cSDimitry Andric 
2055349cc55cSDimitry Andric   // A mapping field is drawn as two text fields with a right arrow in between.
2056349cc55cSDimitry Andric   // The first field stores the key of the mapping and the second stores the
2057349cc55cSDimitry Andric   // value if the mapping.
2058349cc55cSDimitry Andric   //
2059349cc55cSDimitry Andric   // __[Key]_____________   __[Value]___________
2060349cc55cSDimitry Andric   // |                  | > |                  |
2061349cc55cSDimitry Andric   // |__________________|   |__________________|
2062349cc55cSDimitry Andric   // - Error message if it exists.
2063349cc55cSDimitry Andric 
2064349cc55cSDimitry Andric   // The mapping field has a height that is equal to the maximum height between
2065349cc55cSDimitry Andric   // the key and value fields.
2066349cc55cSDimitry Andric   int FieldDelegateGetHeight() override {
2067349cc55cSDimitry Andric     return std::max(m_key_field.FieldDelegateGetHeight(),
2068349cc55cSDimitry Andric                     m_value_field.FieldDelegateGetHeight());
2069349cc55cSDimitry Andric   }
2070349cc55cSDimitry Andric 
2071349cc55cSDimitry Andric   void DrawArrow(Surface &surface) {
2072349cc55cSDimitry Andric     surface.MoveCursor(0, 1);
2073349cc55cSDimitry Andric     surface.PutChar(ACS_RARROW);
2074349cc55cSDimitry Andric   }
2075349cc55cSDimitry Andric 
2076349cc55cSDimitry Andric   void FieldDelegateDraw(Surface &surface, bool is_selected) override {
2077349cc55cSDimitry Andric     Rect bounds = surface.GetFrame();
2078349cc55cSDimitry Andric     Rect key_field_bounds, arrow_and_value_field_bounds;
2079349cc55cSDimitry Andric     bounds.VerticalSplit(bounds.size.width / 2, key_field_bounds,
2080349cc55cSDimitry Andric                          arrow_and_value_field_bounds);
2081349cc55cSDimitry Andric     Rect arrow_bounds, value_field_bounds;
2082349cc55cSDimitry Andric     arrow_and_value_field_bounds.VerticalSplit(1, arrow_bounds,
2083349cc55cSDimitry Andric                                                value_field_bounds);
2084349cc55cSDimitry Andric 
2085349cc55cSDimitry Andric     Surface key_field_surface = surface.SubSurface(key_field_bounds);
2086349cc55cSDimitry Andric     Surface arrow_surface = surface.SubSurface(arrow_bounds);
2087349cc55cSDimitry Andric     Surface value_field_surface = surface.SubSurface(value_field_bounds);
2088349cc55cSDimitry Andric 
2089349cc55cSDimitry Andric     bool key_is_selected =
2090349cc55cSDimitry Andric         m_selection_type == SelectionType::Key && is_selected;
2091349cc55cSDimitry Andric     m_key_field.FieldDelegateDraw(key_field_surface, key_is_selected);
2092349cc55cSDimitry Andric     DrawArrow(arrow_surface);
2093349cc55cSDimitry Andric     bool value_is_selected =
2094349cc55cSDimitry Andric         m_selection_type == SelectionType::Value && is_selected;
2095349cc55cSDimitry Andric     m_value_field.FieldDelegateDraw(value_field_surface, value_is_selected);
2096349cc55cSDimitry Andric   }
2097349cc55cSDimitry Andric 
2098349cc55cSDimitry Andric   HandleCharResult SelectNext(int key) {
2099349cc55cSDimitry Andric     if (FieldDelegateOnLastOrOnlyElement())
2100349cc55cSDimitry Andric       return eKeyNotHandled;
2101349cc55cSDimitry Andric 
2102349cc55cSDimitry Andric     if (!m_key_field.FieldDelegateOnLastOrOnlyElement()) {
2103349cc55cSDimitry Andric       return m_key_field.FieldDelegateHandleChar(key);
2104349cc55cSDimitry Andric     }
2105349cc55cSDimitry Andric 
2106349cc55cSDimitry Andric     m_key_field.FieldDelegateExitCallback();
2107349cc55cSDimitry Andric     m_selection_type = SelectionType::Value;
2108349cc55cSDimitry Andric     m_value_field.FieldDelegateSelectFirstElement();
2109349cc55cSDimitry Andric     return eKeyHandled;
2110349cc55cSDimitry Andric   }
2111349cc55cSDimitry Andric 
2112349cc55cSDimitry Andric   HandleCharResult SelectPrevious(int key) {
2113349cc55cSDimitry Andric     if (FieldDelegateOnFirstOrOnlyElement())
2114349cc55cSDimitry Andric       return eKeyNotHandled;
2115349cc55cSDimitry Andric 
2116349cc55cSDimitry Andric     if (!m_value_field.FieldDelegateOnFirstOrOnlyElement()) {
2117349cc55cSDimitry Andric       return m_value_field.FieldDelegateHandleChar(key);
2118349cc55cSDimitry Andric     }
2119349cc55cSDimitry Andric 
2120349cc55cSDimitry Andric     m_value_field.FieldDelegateExitCallback();
2121349cc55cSDimitry Andric     m_selection_type = SelectionType::Key;
2122349cc55cSDimitry Andric     m_key_field.FieldDelegateSelectLastElement();
2123349cc55cSDimitry Andric     return eKeyHandled;
2124349cc55cSDimitry Andric   }
2125349cc55cSDimitry Andric 
2126349cc55cSDimitry Andric   // If the value field is selected, pass the key to it. If the key field is
2127349cc55cSDimitry Andric   // selected, its last element is selected, and it didn't handle the key, then
2128349cc55cSDimitry Andric   // select its corresponding value field.
2129349cc55cSDimitry Andric   HandleCharResult SelectNextField(int key) {
2130349cc55cSDimitry Andric     if (m_selection_type == SelectionType::Value) {
2131349cc55cSDimitry Andric       return m_value_field.FieldDelegateHandleChar(key);
2132349cc55cSDimitry Andric     }
2133349cc55cSDimitry Andric 
2134349cc55cSDimitry Andric     if (m_key_field.FieldDelegateHandleChar(key) == eKeyHandled)
2135349cc55cSDimitry Andric       return eKeyHandled;
2136349cc55cSDimitry Andric 
2137349cc55cSDimitry Andric     if (!m_key_field.FieldDelegateOnLastOrOnlyElement())
2138349cc55cSDimitry Andric       return eKeyNotHandled;
2139349cc55cSDimitry Andric 
2140349cc55cSDimitry Andric     m_key_field.FieldDelegateExitCallback();
2141349cc55cSDimitry Andric     m_selection_type = SelectionType::Value;
2142349cc55cSDimitry Andric     m_value_field.FieldDelegateSelectFirstElement();
2143349cc55cSDimitry Andric     return eKeyHandled;
2144349cc55cSDimitry Andric   }
2145349cc55cSDimitry Andric 
2146349cc55cSDimitry Andric   HandleCharResult FieldDelegateHandleChar(int key) override {
2147349cc55cSDimitry Andric     switch (key) {
2148349cc55cSDimitry Andric     case KEY_RETURN:
2149349cc55cSDimitry Andric       return SelectNextField(key);
2150349cc55cSDimitry Andric     case '\t':
2151349cc55cSDimitry Andric       return SelectNext(key);
2152349cc55cSDimitry Andric     case KEY_SHIFT_TAB:
2153349cc55cSDimitry Andric       return SelectPrevious(key);
2154349cc55cSDimitry Andric     default:
2155349cc55cSDimitry Andric       break;
2156349cc55cSDimitry Andric     }
2157349cc55cSDimitry Andric 
2158349cc55cSDimitry Andric     // If the key wasn't handled, pass the key to the selected field.
2159349cc55cSDimitry Andric     if (m_selection_type == SelectionType::Key)
2160349cc55cSDimitry Andric       return m_key_field.FieldDelegateHandleChar(key);
2161349cc55cSDimitry Andric     else
2162349cc55cSDimitry Andric       return m_value_field.FieldDelegateHandleChar(key);
2163349cc55cSDimitry Andric 
2164349cc55cSDimitry Andric     return eKeyNotHandled;
2165349cc55cSDimitry Andric   }
2166349cc55cSDimitry Andric 
2167349cc55cSDimitry Andric   bool FieldDelegateOnFirstOrOnlyElement() override {
2168349cc55cSDimitry Andric     return m_selection_type == SelectionType::Key;
2169349cc55cSDimitry Andric   }
2170349cc55cSDimitry Andric 
2171349cc55cSDimitry Andric   bool FieldDelegateOnLastOrOnlyElement() override {
2172349cc55cSDimitry Andric     return m_selection_type == SelectionType::Value;
2173349cc55cSDimitry Andric   }
2174349cc55cSDimitry Andric 
2175349cc55cSDimitry Andric   void FieldDelegateSelectFirstElement() override {
2176349cc55cSDimitry Andric     m_selection_type = SelectionType::Key;
2177349cc55cSDimitry Andric   }
2178349cc55cSDimitry Andric 
2179349cc55cSDimitry Andric   void FieldDelegateSelectLastElement() override {
2180349cc55cSDimitry Andric     m_selection_type = SelectionType::Value;
2181349cc55cSDimitry Andric   }
2182349cc55cSDimitry Andric 
2183349cc55cSDimitry Andric   bool FieldDelegateHasError() override {
2184349cc55cSDimitry Andric     return m_key_field.FieldDelegateHasError() ||
2185349cc55cSDimitry Andric            m_value_field.FieldDelegateHasError();
2186349cc55cSDimitry Andric   }
2187349cc55cSDimitry Andric 
2188349cc55cSDimitry Andric   KeyFieldDelegateType &GetKeyField() { return m_key_field; }
2189349cc55cSDimitry Andric 
2190349cc55cSDimitry Andric   ValueFieldDelegateType &GetValueField() { return m_value_field; }
2191349cc55cSDimitry Andric 
2192349cc55cSDimitry Andric protected:
2193349cc55cSDimitry Andric   KeyFieldDelegateType m_key_field;
2194349cc55cSDimitry Andric   ValueFieldDelegateType m_value_field;
2195349cc55cSDimitry Andric   // See SelectionType class enum.
2196349cc55cSDimitry Andric   SelectionType m_selection_type;
2197349cc55cSDimitry Andric };
2198349cc55cSDimitry Andric 
2199349cc55cSDimitry Andric class EnvironmentVariableNameFieldDelegate : public TextFieldDelegate {
2200349cc55cSDimitry Andric public:
2201349cc55cSDimitry Andric   EnvironmentVariableNameFieldDelegate(const char *content)
2202349cc55cSDimitry Andric       : TextFieldDelegate("Name", content, true) {}
2203349cc55cSDimitry Andric 
2204349cc55cSDimitry Andric   // Environment variable names can't contain an equal sign.
2205349cc55cSDimitry Andric   bool IsAcceptableChar(int key) override {
2206349cc55cSDimitry Andric     return TextFieldDelegate::IsAcceptableChar(key) && key != '=';
2207349cc55cSDimitry Andric   }
2208349cc55cSDimitry Andric 
2209349cc55cSDimitry Andric   const std::string &GetName() { return m_content; }
2210349cc55cSDimitry Andric };
2211349cc55cSDimitry Andric 
2212349cc55cSDimitry Andric class EnvironmentVariableFieldDelegate
2213349cc55cSDimitry Andric     : public MappingFieldDelegate<EnvironmentVariableNameFieldDelegate,
2214349cc55cSDimitry Andric                                   TextFieldDelegate> {
2215349cc55cSDimitry Andric public:
2216349cc55cSDimitry Andric   EnvironmentVariableFieldDelegate()
2217349cc55cSDimitry Andric       : MappingFieldDelegate(
2218349cc55cSDimitry Andric             EnvironmentVariableNameFieldDelegate(""),
2219349cc55cSDimitry Andric             TextFieldDelegate("Value", "", /*required=*/false)) {}
2220349cc55cSDimitry Andric 
2221349cc55cSDimitry Andric   const std::string &GetName() { return GetKeyField().GetName(); }
2222349cc55cSDimitry Andric 
2223349cc55cSDimitry Andric   const std::string &GetValue() { return GetValueField().GetText(); }
2224349cc55cSDimitry Andric 
2225349cc55cSDimitry Andric   void SetName(const char *name) { return GetKeyField().SetText(name); }
2226349cc55cSDimitry Andric 
2227349cc55cSDimitry Andric   void SetValue(const char *value) { return GetValueField().SetText(value); }
2228349cc55cSDimitry Andric };
2229349cc55cSDimitry Andric 
2230349cc55cSDimitry Andric class EnvironmentVariableListFieldDelegate
2231349cc55cSDimitry Andric     : public ListFieldDelegate<EnvironmentVariableFieldDelegate> {
2232349cc55cSDimitry Andric public:
2233349cc55cSDimitry Andric   EnvironmentVariableListFieldDelegate(const char *label)
2234349cc55cSDimitry Andric       : ListFieldDelegate(label, EnvironmentVariableFieldDelegate()) {}
2235349cc55cSDimitry Andric 
2236349cc55cSDimitry Andric   Environment GetEnvironment() {
2237349cc55cSDimitry Andric     Environment environment;
2238349cc55cSDimitry Andric     for (int i = 0; i < GetNumberOfFields(); i++) {
2239349cc55cSDimitry Andric       environment.insert(
2240349cc55cSDimitry Andric           std::make_pair(GetField(i).GetName(), GetField(i).GetValue()));
2241349cc55cSDimitry Andric     }
2242349cc55cSDimitry Andric     return environment;
2243349cc55cSDimitry Andric   }
2244349cc55cSDimitry Andric 
2245349cc55cSDimitry Andric   void AddEnvironmentVariables(const Environment &environment) {
2246349cc55cSDimitry Andric     for (auto &variable : environment) {
2247349cc55cSDimitry Andric       AddNewField();
2248349cc55cSDimitry Andric       EnvironmentVariableFieldDelegate &field =
2249349cc55cSDimitry Andric           GetField(GetNumberOfFields() - 1);
2250349cc55cSDimitry Andric       field.SetName(variable.getKey().str().c_str());
2251349cc55cSDimitry Andric       field.SetValue(variable.getValue().c_str());
2252349cc55cSDimitry Andric     }
2253349cc55cSDimitry Andric   }
2254349cc55cSDimitry Andric };
2255349cc55cSDimitry Andric 
2256fe6060f1SDimitry Andric class FormAction {
2257fe6060f1SDimitry Andric public:
2258fe6060f1SDimitry Andric   FormAction(const char *label, std::function<void(Window &)> action)
2259fe6060f1SDimitry Andric       : m_action(action) {
2260fe6060f1SDimitry Andric     if (label)
2261fe6060f1SDimitry Andric       m_label = label;
2262fe6060f1SDimitry Andric   }
2263fe6060f1SDimitry Andric 
2264fe6060f1SDimitry Andric   // Draw a centered [Label].
2265349cc55cSDimitry Andric   void Draw(Surface &surface, bool is_selected) {
2266fe6060f1SDimitry Andric     int x = (surface.GetWidth() - m_label.length()) / 2;
2267fe6060f1SDimitry Andric     surface.MoveCursor(x, 0);
2268fe6060f1SDimitry Andric     if (is_selected)
2269fe6060f1SDimitry Andric       surface.AttributeOn(A_REVERSE);
2270fe6060f1SDimitry Andric     surface.PutChar('[');
2271fe6060f1SDimitry Andric     surface.PutCString(m_label.c_str());
2272fe6060f1SDimitry Andric     surface.PutChar(']');
2273fe6060f1SDimitry Andric     if (is_selected)
2274fe6060f1SDimitry Andric       surface.AttributeOff(A_REVERSE);
2275fe6060f1SDimitry Andric   }
2276fe6060f1SDimitry Andric 
2277fe6060f1SDimitry Andric   void Execute(Window &window) { m_action(window); }
2278fe6060f1SDimitry Andric 
2279fe6060f1SDimitry Andric   const std::string &GetLabel() { return m_label; }
2280fe6060f1SDimitry Andric 
2281fe6060f1SDimitry Andric protected:
2282fe6060f1SDimitry Andric   std::string m_label;
2283fe6060f1SDimitry Andric   std::function<void(Window &)> m_action;
2284fe6060f1SDimitry Andric };
2285fe6060f1SDimitry Andric 
2286fe6060f1SDimitry Andric class FormDelegate {
2287fe6060f1SDimitry Andric public:
228881ad6265SDimitry Andric   FormDelegate() = default;
2289fe6060f1SDimitry Andric 
2290fe6060f1SDimitry Andric   virtual ~FormDelegate() = default;
2291fe6060f1SDimitry Andric 
2292fe6060f1SDimitry Andric   virtual std::string GetName() = 0;
2293fe6060f1SDimitry Andric 
22940eae32dcSDimitry Andric   virtual void UpdateFieldsVisibility() {}
2295fe6060f1SDimitry Andric 
2296fe6060f1SDimitry Andric   FieldDelegate *GetField(uint32_t field_index) {
2297fe6060f1SDimitry Andric     if (field_index < m_fields.size())
2298fe6060f1SDimitry Andric       return m_fields[field_index].get();
2299fe6060f1SDimitry Andric     return nullptr;
2300fe6060f1SDimitry Andric   }
2301fe6060f1SDimitry Andric 
2302fe6060f1SDimitry Andric   FormAction &GetAction(int action_index) { return m_actions[action_index]; }
2303fe6060f1SDimitry Andric 
2304fe6060f1SDimitry Andric   int GetNumberOfFields() { return m_fields.size(); }
2305fe6060f1SDimitry Andric 
2306fe6060f1SDimitry Andric   int GetNumberOfActions() { return m_actions.size(); }
2307fe6060f1SDimitry Andric 
2308fe6060f1SDimitry Andric   bool HasError() { return !m_error.empty(); }
2309fe6060f1SDimitry Andric 
2310fe6060f1SDimitry Andric   void ClearError() { m_error.clear(); }
2311fe6060f1SDimitry Andric 
2312fe6060f1SDimitry Andric   const std::string &GetError() { return m_error; }
2313fe6060f1SDimitry Andric 
2314fe6060f1SDimitry Andric   void SetError(const char *error) { m_error = error; }
2315fe6060f1SDimitry Andric 
2316fe6060f1SDimitry Andric   // If all fields are valid, true is returned. Otherwise, an error message is
2317fe6060f1SDimitry Andric   // set and false is returned. This method is usually called at the start of an
2318fe6060f1SDimitry Andric   // action that requires valid fields.
2319fe6060f1SDimitry Andric   bool CheckFieldsValidity() {
2320fe6060f1SDimitry Andric     for (int i = 0; i < GetNumberOfFields(); i++) {
2321349cc55cSDimitry Andric       GetField(i)->FieldDelegateExitCallback();
2322fe6060f1SDimitry Andric       if (GetField(i)->FieldDelegateHasError()) {
2323fe6060f1SDimitry Andric         SetError("Some fields are invalid!");
2324fe6060f1SDimitry Andric         return false;
2325fe6060f1SDimitry Andric       }
2326fe6060f1SDimitry Andric     }
2327fe6060f1SDimitry Andric     return true;
2328fe6060f1SDimitry Andric   }
2329fe6060f1SDimitry Andric 
2330fe6060f1SDimitry Andric   // Factory methods to create and add fields of specific types.
2331fe6060f1SDimitry Andric 
2332fe6060f1SDimitry Andric   TextFieldDelegate *AddTextField(const char *label, const char *content,
2333fe6060f1SDimitry Andric                                   bool required) {
2334fe6060f1SDimitry Andric     TextFieldDelegate *delegate =
2335fe6060f1SDimitry Andric         new TextFieldDelegate(label, content, required);
2336fe6060f1SDimitry Andric     m_fields.push_back(FieldDelegateUP(delegate));
2337fe6060f1SDimitry Andric     return delegate;
2338fe6060f1SDimitry Andric   }
2339fe6060f1SDimitry Andric 
2340fe6060f1SDimitry Andric   FileFieldDelegate *AddFileField(const char *label, const char *content,
2341fe6060f1SDimitry Andric                                   bool need_to_exist, bool required) {
2342fe6060f1SDimitry Andric     FileFieldDelegate *delegate =
2343fe6060f1SDimitry Andric         new FileFieldDelegate(label, content, need_to_exist, required);
2344fe6060f1SDimitry Andric     m_fields.push_back(FieldDelegateUP(delegate));
2345fe6060f1SDimitry Andric     return delegate;
2346fe6060f1SDimitry Andric   }
2347fe6060f1SDimitry Andric 
2348fe6060f1SDimitry Andric   DirectoryFieldDelegate *AddDirectoryField(const char *label,
2349fe6060f1SDimitry Andric                                             const char *content,
2350fe6060f1SDimitry Andric                                             bool need_to_exist, bool required) {
2351fe6060f1SDimitry Andric     DirectoryFieldDelegate *delegate =
2352fe6060f1SDimitry Andric         new DirectoryFieldDelegate(label, content, need_to_exist, required);
2353fe6060f1SDimitry Andric     m_fields.push_back(FieldDelegateUP(delegate));
2354fe6060f1SDimitry Andric     return delegate;
2355fe6060f1SDimitry Andric   }
2356fe6060f1SDimitry Andric 
2357fe6060f1SDimitry Andric   ArchFieldDelegate *AddArchField(const char *label, const char *content,
2358fe6060f1SDimitry Andric                                   bool required) {
2359fe6060f1SDimitry Andric     ArchFieldDelegate *delegate =
2360fe6060f1SDimitry Andric         new ArchFieldDelegate(label, content, required);
2361fe6060f1SDimitry Andric     m_fields.push_back(FieldDelegateUP(delegate));
2362fe6060f1SDimitry Andric     return delegate;
2363fe6060f1SDimitry Andric   }
2364fe6060f1SDimitry Andric 
2365fe6060f1SDimitry Andric   IntegerFieldDelegate *AddIntegerField(const char *label, int content,
2366fe6060f1SDimitry Andric                                         bool required) {
2367fe6060f1SDimitry Andric     IntegerFieldDelegate *delegate =
2368fe6060f1SDimitry Andric         new IntegerFieldDelegate(label, content, required);
2369fe6060f1SDimitry Andric     m_fields.push_back(FieldDelegateUP(delegate));
2370fe6060f1SDimitry Andric     return delegate;
2371fe6060f1SDimitry Andric   }
2372fe6060f1SDimitry Andric 
2373fe6060f1SDimitry Andric   BooleanFieldDelegate *AddBooleanField(const char *label, bool content) {
2374fe6060f1SDimitry Andric     BooleanFieldDelegate *delegate = new BooleanFieldDelegate(label, content);
2375fe6060f1SDimitry Andric     m_fields.push_back(FieldDelegateUP(delegate));
2376fe6060f1SDimitry Andric     return delegate;
2377fe6060f1SDimitry Andric   }
2378fe6060f1SDimitry Andric 
2379349cc55cSDimitry Andric   LazyBooleanFieldDelegate *AddLazyBooleanField(const char *label,
2380349cc55cSDimitry Andric                                                 const char *calculate_label) {
2381349cc55cSDimitry Andric     LazyBooleanFieldDelegate *delegate =
2382349cc55cSDimitry Andric         new LazyBooleanFieldDelegate(label, calculate_label);
2383349cc55cSDimitry Andric     m_fields.push_back(FieldDelegateUP(delegate));
2384349cc55cSDimitry Andric     return delegate;
2385349cc55cSDimitry Andric   }
2386349cc55cSDimitry Andric 
2387fe6060f1SDimitry Andric   ChoicesFieldDelegate *AddChoicesField(const char *label, int height,
2388fe6060f1SDimitry Andric                                         std::vector<std::string> choices) {
2389fe6060f1SDimitry Andric     ChoicesFieldDelegate *delegate =
2390fe6060f1SDimitry Andric         new ChoicesFieldDelegate(label, height, choices);
2391fe6060f1SDimitry Andric     m_fields.push_back(FieldDelegateUP(delegate));
2392fe6060f1SDimitry Andric     return delegate;
2393fe6060f1SDimitry Andric   }
2394fe6060f1SDimitry Andric 
2395fe6060f1SDimitry Andric   PlatformPluginFieldDelegate *AddPlatformPluginField(Debugger &debugger) {
2396fe6060f1SDimitry Andric     PlatformPluginFieldDelegate *delegate =
2397fe6060f1SDimitry Andric         new PlatformPluginFieldDelegate(debugger);
2398fe6060f1SDimitry Andric     m_fields.push_back(FieldDelegateUP(delegate));
2399fe6060f1SDimitry Andric     return delegate;
2400fe6060f1SDimitry Andric   }
2401fe6060f1SDimitry Andric 
2402fe6060f1SDimitry Andric   ProcessPluginFieldDelegate *AddProcessPluginField() {
2403fe6060f1SDimitry Andric     ProcessPluginFieldDelegate *delegate = new ProcessPluginFieldDelegate();
2404fe6060f1SDimitry Andric     m_fields.push_back(FieldDelegateUP(delegate));
2405fe6060f1SDimitry Andric     return delegate;
2406fe6060f1SDimitry Andric   }
2407fe6060f1SDimitry Andric 
2408fe6060f1SDimitry Andric   template <class T>
2409fe6060f1SDimitry Andric   ListFieldDelegate<T> *AddListField(const char *label, T default_field) {
2410fe6060f1SDimitry Andric     ListFieldDelegate<T> *delegate =
2411fe6060f1SDimitry Andric         new ListFieldDelegate<T>(label, default_field);
2412fe6060f1SDimitry Andric     m_fields.push_back(FieldDelegateUP(delegate));
2413fe6060f1SDimitry Andric     return delegate;
2414fe6060f1SDimitry Andric   }
2415fe6060f1SDimitry Andric 
2416349cc55cSDimitry Andric   ArgumentsFieldDelegate *AddArgumentsField() {
2417349cc55cSDimitry Andric     ArgumentsFieldDelegate *delegate = new ArgumentsFieldDelegate();
2418349cc55cSDimitry Andric     m_fields.push_back(FieldDelegateUP(delegate));
2419349cc55cSDimitry Andric     return delegate;
2420349cc55cSDimitry Andric   }
2421349cc55cSDimitry Andric 
2422349cc55cSDimitry Andric   template <class K, class V>
2423349cc55cSDimitry Andric   MappingFieldDelegate<K, V> *AddMappingField(K key_field, V value_field) {
2424349cc55cSDimitry Andric     MappingFieldDelegate<K, V> *delegate =
2425349cc55cSDimitry Andric         new MappingFieldDelegate<K, V>(key_field, value_field);
2426349cc55cSDimitry Andric     m_fields.push_back(FieldDelegateUP(delegate));
2427349cc55cSDimitry Andric     return delegate;
2428349cc55cSDimitry Andric   }
2429349cc55cSDimitry Andric 
2430349cc55cSDimitry Andric   EnvironmentVariableNameFieldDelegate *
2431349cc55cSDimitry Andric   AddEnvironmentVariableNameField(const char *content) {
2432349cc55cSDimitry Andric     EnvironmentVariableNameFieldDelegate *delegate =
2433349cc55cSDimitry Andric         new EnvironmentVariableNameFieldDelegate(content);
2434349cc55cSDimitry Andric     m_fields.push_back(FieldDelegateUP(delegate));
2435349cc55cSDimitry Andric     return delegate;
2436349cc55cSDimitry Andric   }
2437349cc55cSDimitry Andric 
2438349cc55cSDimitry Andric   EnvironmentVariableFieldDelegate *AddEnvironmentVariableField() {
2439349cc55cSDimitry Andric     EnvironmentVariableFieldDelegate *delegate =
2440349cc55cSDimitry Andric         new EnvironmentVariableFieldDelegate();
2441349cc55cSDimitry Andric     m_fields.push_back(FieldDelegateUP(delegate));
2442349cc55cSDimitry Andric     return delegate;
2443349cc55cSDimitry Andric   }
2444349cc55cSDimitry Andric 
2445349cc55cSDimitry Andric   EnvironmentVariableListFieldDelegate *
2446349cc55cSDimitry Andric   AddEnvironmentVariableListField(const char *label) {
2447349cc55cSDimitry Andric     EnvironmentVariableListFieldDelegate *delegate =
2448349cc55cSDimitry Andric         new EnvironmentVariableListFieldDelegate(label);
2449349cc55cSDimitry Andric     m_fields.push_back(FieldDelegateUP(delegate));
2450349cc55cSDimitry Andric     return delegate;
2451349cc55cSDimitry Andric   }
2452349cc55cSDimitry Andric 
2453fe6060f1SDimitry Andric   // Factory methods for adding actions.
2454fe6060f1SDimitry Andric 
2455fe6060f1SDimitry Andric   void AddAction(const char *label, std::function<void(Window &)> action) {
2456fe6060f1SDimitry Andric     m_actions.push_back(FormAction(label, action));
2457fe6060f1SDimitry Andric   }
2458fe6060f1SDimitry Andric 
2459fe6060f1SDimitry Andric protected:
2460fe6060f1SDimitry Andric   std::vector<FieldDelegateUP> m_fields;
2461fe6060f1SDimitry Andric   std::vector<FormAction> m_actions;
2462fe6060f1SDimitry Andric   // Optional error message. If empty, form is considered to have no error.
2463fe6060f1SDimitry Andric   std::string m_error;
2464fe6060f1SDimitry Andric };
2465fe6060f1SDimitry Andric 
2466fe6060f1SDimitry Andric typedef std::shared_ptr<FormDelegate> FormDelegateSP;
2467fe6060f1SDimitry Andric 
2468fe6060f1SDimitry Andric class FormWindowDelegate : public WindowDelegate {
2469fe6060f1SDimitry Andric public:
247081ad6265SDimitry Andric   FormWindowDelegate(FormDelegateSP &delegate_sp) : m_delegate_sp(delegate_sp) {
2471fe6060f1SDimitry Andric     assert(m_delegate_sp->GetNumberOfActions() > 0);
2472fe6060f1SDimitry Andric     if (m_delegate_sp->GetNumberOfFields() > 0)
2473fe6060f1SDimitry Andric       m_selection_type = SelectionType::Field;
2474fe6060f1SDimitry Andric     else
2475fe6060f1SDimitry Andric       m_selection_type = SelectionType::Action;
2476fe6060f1SDimitry Andric   }
2477fe6060f1SDimitry Andric 
2478fe6060f1SDimitry Andric   // Signify which element is selected. If a field or an action is selected,
2479fe6060f1SDimitry Andric   // then m_selection_index signifies the particular field or action that is
2480fe6060f1SDimitry Andric   // selected.
2481fe6060f1SDimitry Andric   enum class SelectionType { Field, Action };
2482fe6060f1SDimitry Andric 
2483fe6060f1SDimitry Andric   // A form window is padded by one character from all sides. First, if an error
2484fe6060f1SDimitry Andric   // message exists, it is drawn followed by a separator. Then one or more
2485fe6060f1SDimitry Andric   // fields are drawn. Finally, all available actions are drawn on a single
2486fe6060f1SDimitry Andric   // line.
2487fe6060f1SDimitry Andric   //
2488fe6060f1SDimitry Andric   // ___<Form Name>_________________________________________________
2489fe6060f1SDimitry Andric   // |                                                             |
2490fe6060f1SDimitry Andric   // | - Error message if it exists.                               |
2491fe6060f1SDimitry Andric   // |-------------------------------------------------------------|
2492fe6060f1SDimitry Andric   // | Form elements here.                                         |
2493fe6060f1SDimitry Andric   // |                       Form actions here.                    |
2494fe6060f1SDimitry Andric   // |                                                             |
2495fe6060f1SDimitry Andric   // |______________________________________[Press Esc to cancel]__|
2496fe6060f1SDimitry Andric   //
2497fe6060f1SDimitry Andric 
2498fe6060f1SDimitry Andric   // One line for the error and another for the horizontal line.
2499fe6060f1SDimitry Andric   int GetErrorHeight() {
2500fe6060f1SDimitry Andric     if (m_delegate_sp->HasError())
2501fe6060f1SDimitry Andric       return 2;
2502fe6060f1SDimitry Andric     return 0;
2503fe6060f1SDimitry Andric   }
2504fe6060f1SDimitry Andric 
2505fe6060f1SDimitry Andric   // Actions span a single line.
2506fe6060f1SDimitry Andric   int GetActionsHeight() {
2507fe6060f1SDimitry Andric     if (m_delegate_sp->GetNumberOfActions() > 0)
2508fe6060f1SDimitry Andric       return 1;
2509fe6060f1SDimitry Andric     return 0;
2510fe6060f1SDimitry Andric   }
2511fe6060f1SDimitry Andric 
2512fe6060f1SDimitry Andric   // Get the total number of needed lines to draw the contents.
2513fe6060f1SDimitry Andric   int GetContentHeight() {
2514fe6060f1SDimitry Andric     int height = 0;
2515fe6060f1SDimitry Andric     height += GetErrorHeight();
2516fe6060f1SDimitry Andric     for (int i = 0; i < m_delegate_sp->GetNumberOfFields(); i++) {
2517fe6060f1SDimitry Andric       if (!m_delegate_sp->GetField(i)->FieldDelegateIsVisible())
2518fe6060f1SDimitry Andric         continue;
2519fe6060f1SDimitry Andric       height += m_delegate_sp->GetField(i)->FieldDelegateGetHeight();
2520fe6060f1SDimitry Andric     }
2521fe6060f1SDimitry Andric     height += GetActionsHeight();
2522fe6060f1SDimitry Andric     return height;
2523fe6060f1SDimitry Andric   }
2524fe6060f1SDimitry Andric 
2525fe6060f1SDimitry Andric   ScrollContext GetScrollContext() {
2526fe6060f1SDimitry Andric     if (m_selection_type == SelectionType::Action)
2527fe6060f1SDimitry Andric       return ScrollContext(GetContentHeight() - 1);
2528fe6060f1SDimitry Andric 
2529fe6060f1SDimitry Andric     FieldDelegate *field = m_delegate_sp->GetField(m_selection_index);
2530fe6060f1SDimitry Andric     ScrollContext context = field->FieldDelegateGetScrollContext();
2531fe6060f1SDimitry Andric 
2532fe6060f1SDimitry Andric     int offset = GetErrorHeight();
2533fe6060f1SDimitry Andric     for (int i = 0; i < m_selection_index; i++) {
2534fe6060f1SDimitry Andric       if (!m_delegate_sp->GetField(i)->FieldDelegateIsVisible())
2535fe6060f1SDimitry Andric         continue;
2536fe6060f1SDimitry Andric       offset += m_delegate_sp->GetField(i)->FieldDelegateGetHeight();
2537fe6060f1SDimitry Andric     }
2538fe6060f1SDimitry Andric     context.Offset(offset);
2539fe6060f1SDimitry Andric 
2540fe6060f1SDimitry Andric     // If the context is touching the error, include the error in the context as
2541fe6060f1SDimitry Andric     // well.
2542fe6060f1SDimitry Andric     if (context.start == GetErrorHeight())
2543fe6060f1SDimitry Andric       context.start = 0;
2544fe6060f1SDimitry Andric 
2545fe6060f1SDimitry Andric     return context;
2546fe6060f1SDimitry Andric   }
2547fe6060f1SDimitry Andric 
2548349cc55cSDimitry Andric   void UpdateScrolling(Surface &surface) {
2549fe6060f1SDimitry Andric     ScrollContext context = GetScrollContext();
2550fe6060f1SDimitry Andric     int content_height = GetContentHeight();
2551fe6060f1SDimitry Andric     int surface_height = surface.GetHeight();
2552fe6060f1SDimitry Andric     int visible_height = std::min(content_height, surface_height);
2553fe6060f1SDimitry Andric     int last_visible_line = m_first_visible_line + visible_height - 1;
2554fe6060f1SDimitry Andric 
2555fe6060f1SDimitry Andric     // If the last visible line is bigger than the content, then it is invalid
2556fe6060f1SDimitry Andric     // and needs to be set to the last line in the content. This can happen when
2557fe6060f1SDimitry Andric     // a field has shrunk in height.
2558fe6060f1SDimitry Andric     if (last_visible_line > content_height - 1) {
2559fe6060f1SDimitry Andric       m_first_visible_line = content_height - visible_height;
2560fe6060f1SDimitry Andric     }
2561fe6060f1SDimitry Andric 
2562fe6060f1SDimitry Andric     if (context.start < m_first_visible_line) {
2563fe6060f1SDimitry Andric       m_first_visible_line = context.start;
2564fe6060f1SDimitry Andric       return;
2565fe6060f1SDimitry Andric     }
2566fe6060f1SDimitry Andric 
2567fe6060f1SDimitry Andric     if (context.end > last_visible_line) {
2568fe6060f1SDimitry Andric       m_first_visible_line = context.end - visible_height + 1;
2569fe6060f1SDimitry Andric     }
2570fe6060f1SDimitry Andric   }
2571fe6060f1SDimitry Andric 
2572349cc55cSDimitry Andric   void DrawError(Surface &surface) {
2573fe6060f1SDimitry Andric     if (!m_delegate_sp->HasError())
2574fe6060f1SDimitry Andric       return;
2575fe6060f1SDimitry Andric     surface.MoveCursor(0, 0);
2576fe6060f1SDimitry Andric     surface.AttributeOn(COLOR_PAIR(RedOnBlack));
2577fe6060f1SDimitry Andric     surface.PutChar(ACS_DIAMOND);
2578fe6060f1SDimitry Andric     surface.PutChar(' ');
2579fe6060f1SDimitry Andric     surface.PutCStringTruncated(1, m_delegate_sp->GetError().c_str());
2580fe6060f1SDimitry Andric     surface.AttributeOff(COLOR_PAIR(RedOnBlack));
2581fe6060f1SDimitry Andric 
2582fe6060f1SDimitry Andric     surface.MoveCursor(0, 1);
2583fe6060f1SDimitry Andric     surface.HorizontalLine(surface.GetWidth());
2584fe6060f1SDimitry Andric   }
2585fe6060f1SDimitry Andric 
2586349cc55cSDimitry Andric   void DrawFields(Surface &surface) {
2587fe6060f1SDimitry Andric     int line = 0;
2588fe6060f1SDimitry Andric     int width = surface.GetWidth();
2589fe6060f1SDimitry Andric     bool a_field_is_selected = m_selection_type == SelectionType::Field;
2590fe6060f1SDimitry Andric     for (int i = 0; i < m_delegate_sp->GetNumberOfFields(); i++) {
2591fe6060f1SDimitry Andric       FieldDelegate *field = m_delegate_sp->GetField(i);
2592fe6060f1SDimitry Andric       if (!field->FieldDelegateIsVisible())
2593fe6060f1SDimitry Andric         continue;
2594fe6060f1SDimitry Andric       bool is_field_selected = a_field_is_selected && m_selection_index == i;
2595fe6060f1SDimitry Andric       int height = field->FieldDelegateGetHeight();
2596fe6060f1SDimitry Andric       Rect bounds = Rect(Point(0, line), Size(width, height));
2597349cc55cSDimitry Andric       Surface field_surface = surface.SubSurface(bounds);
2598fe6060f1SDimitry Andric       field->FieldDelegateDraw(field_surface, is_field_selected);
2599fe6060f1SDimitry Andric       line += height;
2600fe6060f1SDimitry Andric     }
2601fe6060f1SDimitry Andric   }
2602fe6060f1SDimitry Andric 
2603349cc55cSDimitry Andric   void DrawActions(Surface &surface) {
2604fe6060f1SDimitry Andric     int number_of_actions = m_delegate_sp->GetNumberOfActions();
2605fe6060f1SDimitry Andric     int width = surface.GetWidth() / number_of_actions;
2606fe6060f1SDimitry Andric     bool an_action_is_selected = m_selection_type == SelectionType::Action;
2607fe6060f1SDimitry Andric     int x = 0;
2608fe6060f1SDimitry Andric     for (int i = 0; i < number_of_actions; i++) {
2609fe6060f1SDimitry Andric       bool is_action_selected = an_action_is_selected && m_selection_index == i;
2610fe6060f1SDimitry Andric       FormAction &action = m_delegate_sp->GetAction(i);
2611fe6060f1SDimitry Andric       Rect bounds = Rect(Point(x, 0), Size(width, 1));
2612349cc55cSDimitry Andric       Surface action_surface = surface.SubSurface(bounds);
2613fe6060f1SDimitry Andric       action.Draw(action_surface, is_action_selected);
2614fe6060f1SDimitry Andric       x += width;
2615fe6060f1SDimitry Andric     }
2616fe6060f1SDimitry Andric   }
2617fe6060f1SDimitry Andric 
2618349cc55cSDimitry Andric   void DrawElements(Surface &surface) {
2619fe6060f1SDimitry Andric     Rect frame = surface.GetFrame();
2620fe6060f1SDimitry Andric     Rect fields_bounds, actions_bounds;
2621fe6060f1SDimitry Andric     frame.HorizontalSplit(surface.GetHeight() - GetActionsHeight(),
2622fe6060f1SDimitry Andric                           fields_bounds, actions_bounds);
2623349cc55cSDimitry Andric     Surface fields_surface = surface.SubSurface(fields_bounds);
2624349cc55cSDimitry Andric     Surface actions_surface = surface.SubSurface(actions_bounds);
2625fe6060f1SDimitry Andric 
2626fe6060f1SDimitry Andric     DrawFields(fields_surface);
2627fe6060f1SDimitry Andric     DrawActions(actions_surface);
2628fe6060f1SDimitry Andric   }
2629fe6060f1SDimitry Andric 
2630fe6060f1SDimitry Andric   // Contents are first drawn on a pad. Then a subset of that pad is copied to
2631fe6060f1SDimitry Andric   // the derived window starting at the first visible line. This essentially
2632fe6060f1SDimitry Andric   // provides scrolling functionality.
2633349cc55cSDimitry Andric   void DrawContent(Surface &surface) {
2634fe6060f1SDimitry Andric     UpdateScrolling(surface);
2635fe6060f1SDimitry Andric 
2636fe6060f1SDimitry Andric     int width = surface.GetWidth();
2637fe6060f1SDimitry Andric     int height = GetContentHeight();
2638fe6060f1SDimitry Andric     Pad pad = Pad(Size(width, height));
2639fe6060f1SDimitry Andric 
2640fe6060f1SDimitry Andric     Rect frame = pad.GetFrame();
2641fe6060f1SDimitry Andric     Rect error_bounds, elements_bounds;
2642fe6060f1SDimitry Andric     frame.HorizontalSplit(GetErrorHeight(), error_bounds, elements_bounds);
2643349cc55cSDimitry Andric     Surface error_surface = pad.SubSurface(error_bounds);
2644349cc55cSDimitry Andric     Surface elements_surface = pad.SubSurface(elements_bounds);
2645fe6060f1SDimitry Andric 
2646fe6060f1SDimitry Andric     DrawError(error_surface);
2647fe6060f1SDimitry Andric     DrawElements(elements_surface);
2648fe6060f1SDimitry Andric 
2649fe6060f1SDimitry Andric     int copy_height = std::min(surface.GetHeight(), pad.GetHeight());
2650fe6060f1SDimitry Andric     pad.CopyToSurface(surface, Point(0, m_first_visible_line), Point(),
2651fe6060f1SDimitry Andric                       Size(width, copy_height));
2652fe6060f1SDimitry Andric   }
2653fe6060f1SDimitry Andric 
2654349cc55cSDimitry Andric   void DrawSubmitHint(Surface &surface, bool is_active) {
2655349cc55cSDimitry Andric     surface.MoveCursor(2, surface.GetHeight() - 1);
2656349cc55cSDimitry Andric     if (is_active)
2657349cc55cSDimitry Andric       surface.AttributeOn(A_BOLD | COLOR_PAIR(BlackOnWhite));
2658349cc55cSDimitry Andric     surface.Printf("[Press Alt+Enter to %s]",
2659349cc55cSDimitry Andric                    m_delegate_sp->GetAction(0).GetLabel().c_str());
2660349cc55cSDimitry Andric     if (is_active)
2661349cc55cSDimitry Andric       surface.AttributeOff(A_BOLD | COLOR_PAIR(BlackOnWhite));
2662349cc55cSDimitry Andric   }
2663349cc55cSDimitry Andric 
2664fe6060f1SDimitry Andric   bool WindowDelegateDraw(Window &window, bool force) override {
2665fe6060f1SDimitry Andric     m_delegate_sp->UpdateFieldsVisibility();
2666fe6060f1SDimitry Andric 
2667fe6060f1SDimitry Andric     window.Erase();
2668fe6060f1SDimitry Andric 
2669fe6060f1SDimitry Andric     window.DrawTitleBox(m_delegate_sp->GetName().c_str(),
2670349cc55cSDimitry Andric                         "Press Esc to Cancel");
2671349cc55cSDimitry Andric     DrawSubmitHint(window, window.IsActive());
2672fe6060f1SDimitry Andric 
2673fe6060f1SDimitry Andric     Rect content_bounds = window.GetFrame();
2674fe6060f1SDimitry Andric     content_bounds.Inset(2, 2);
2675349cc55cSDimitry Andric     Surface content_surface = window.SubSurface(content_bounds);
2676fe6060f1SDimitry Andric 
2677fe6060f1SDimitry Andric     DrawContent(content_surface);
2678fe6060f1SDimitry Andric     return true;
2679fe6060f1SDimitry Andric   }
2680fe6060f1SDimitry Andric 
2681fe6060f1SDimitry Andric   void SkipNextHiddenFields() {
2682fe6060f1SDimitry Andric     while (true) {
2683fe6060f1SDimitry Andric       if (m_delegate_sp->GetField(m_selection_index)->FieldDelegateIsVisible())
2684fe6060f1SDimitry Andric         return;
2685fe6060f1SDimitry Andric 
2686fe6060f1SDimitry Andric       if (m_selection_index == m_delegate_sp->GetNumberOfFields() - 1) {
2687fe6060f1SDimitry Andric         m_selection_type = SelectionType::Action;
2688fe6060f1SDimitry Andric         m_selection_index = 0;
2689fe6060f1SDimitry Andric         return;
2690fe6060f1SDimitry Andric       }
2691fe6060f1SDimitry Andric 
2692fe6060f1SDimitry Andric       m_selection_index++;
2693fe6060f1SDimitry Andric     }
2694fe6060f1SDimitry Andric   }
2695fe6060f1SDimitry Andric 
2696fe6060f1SDimitry Andric   HandleCharResult SelectNext(int key) {
2697fe6060f1SDimitry Andric     if (m_selection_type == SelectionType::Action) {
2698fe6060f1SDimitry Andric       if (m_selection_index < m_delegate_sp->GetNumberOfActions() - 1) {
2699fe6060f1SDimitry Andric         m_selection_index++;
2700fe6060f1SDimitry Andric         return eKeyHandled;
2701fe6060f1SDimitry Andric       }
2702fe6060f1SDimitry Andric 
2703fe6060f1SDimitry Andric       m_selection_index = 0;
2704fe6060f1SDimitry Andric       m_selection_type = SelectionType::Field;
2705fe6060f1SDimitry Andric       SkipNextHiddenFields();
2706fe6060f1SDimitry Andric       if (m_selection_type == SelectionType::Field) {
2707fe6060f1SDimitry Andric         FieldDelegate *next_field = m_delegate_sp->GetField(m_selection_index);
2708fe6060f1SDimitry Andric         next_field->FieldDelegateSelectFirstElement();
2709fe6060f1SDimitry Andric       }
2710fe6060f1SDimitry Andric       return eKeyHandled;
2711fe6060f1SDimitry Andric     }
2712fe6060f1SDimitry Andric 
2713fe6060f1SDimitry Andric     FieldDelegate *field = m_delegate_sp->GetField(m_selection_index);
2714fe6060f1SDimitry Andric     if (!field->FieldDelegateOnLastOrOnlyElement()) {
2715fe6060f1SDimitry Andric       return field->FieldDelegateHandleChar(key);
2716fe6060f1SDimitry Andric     }
2717fe6060f1SDimitry Andric 
2718fe6060f1SDimitry Andric     field->FieldDelegateExitCallback();
2719fe6060f1SDimitry Andric 
2720fe6060f1SDimitry Andric     if (m_selection_index == m_delegate_sp->GetNumberOfFields() - 1) {
2721fe6060f1SDimitry Andric       m_selection_type = SelectionType::Action;
2722fe6060f1SDimitry Andric       m_selection_index = 0;
2723fe6060f1SDimitry Andric       return eKeyHandled;
2724fe6060f1SDimitry Andric     }
2725fe6060f1SDimitry Andric 
2726fe6060f1SDimitry Andric     m_selection_index++;
2727fe6060f1SDimitry Andric     SkipNextHiddenFields();
2728fe6060f1SDimitry Andric 
2729fe6060f1SDimitry Andric     if (m_selection_type == SelectionType::Field) {
2730fe6060f1SDimitry Andric       FieldDelegate *next_field = m_delegate_sp->GetField(m_selection_index);
2731fe6060f1SDimitry Andric       next_field->FieldDelegateSelectFirstElement();
2732fe6060f1SDimitry Andric     }
2733fe6060f1SDimitry Andric 
2734fe6060f1SDimitry Andric     return eKeyHandled;
2735fe6060f1SDimitry Andric   }
2736fe6060f1SDimitry Andric 
2737fe6060f1SDimitry Andric   void SkipPreviousHiddenFields() {
2738fe6060f1SDimitry Andric     while (true) {
2739fe6060f1SDimitry Andric       if (m_delegate_sp->GetField(m_selection_index)->FieldDelegateIsVisible())
2740fe6060f1SDimitry Andric         return;
2741fe6060f1SDimitry Andric 
2742fe6060f1SDimitry Andric       if (m_selection_index == 0) {
2743fe6060f1SDimitry Andric         m_selection_type = SelectionType::Action;
2744fe6060f1SDimitry Andric         m_selection_index = 0;
2745fe6060f1SDimitry Andric         return;
2746fe6060f1SDimitry Andric       }
2747fe6060f1SDimitry Andric 
2748fe6060f1SDimitry Andric       m_selection_index--;
2749fe6060f1SDimitry Andric     }
2750fe6060f1SDimitry Andric   }
2751fe6060f1SDimitry Andric 
2752fe6060f1SDimitry Andric   HandleCharResult SelectPrevious(int key) {
2753fe6060f1SDimitry Andric     if (m_selection_type == SelectionType::Action) {
2754fe6060f1SDimitry Andric       if (m_selection_index > 0) {
2755fe6060f1SDimitry Andric         m_selection_index--;
2756fe6060f1SDimitry Andric         return eKeyHandled;
2757fe6060f1SDimitry Andric       }
2758fe6060f1SDimitry Andric       m_selection_index = m_delegate_sp->GetNumberOfFields() - 1;
2759fe6060f1SDimitry Andric       m_selection_type = SelectionType::Field;
2760fe6060f1SDimitry Andric       SkipPreviousHiddenFields();
2761fe6060f1SDimitry Andric       if (m_selection_type == SelectionType::Field) {
2762fe6060f1SDimitry Andric         FieldDelegate *previous_field =
2763fe6060f1SDimitry Andric             m_delegate_sp->GetField(m_selection_index);
2764fe6060f1SDimitry Andric         previous_field->FieldDelegateSelectLastElement();
2765fe6060f1SDimitry Andric       }
2766fe6060f1SDimitry Andric       return eKeyHandled;
2767fe6060f1SDimitry Andric     }
2768fe6060f1SDimitry Andric 
2769fe6060f1SDimitry Andric     FieldDelegate *field = m_delegate_sp->GetField(m_selection_index);
2770fe6060f1SDimitry Andric     if (!field->FieldDelegateOnFirstOrOnlyElement()) {
2771fe6060f1SDimitry Andric       return field->FieldDelegateHandleChar(key);
2772fe6060f1SDimitry Andric     }
2773fe6060f1SDimitry Andric 
2774fe6060f1SDimitry Andric     field->FieldDelegateExitCallback();
2775fe6060f1SDimitry Andric 
2776fe6060f1SDimitry Andric     if (m_selection_index == 0) {
2777fe6060f1SDimitry Andric       m_selection_type = SelectionType::Action;
2778fe6060f1SDimitry Andric       m_selection_index = m_delegate_sp->GetNumberOfActions() - 1;
2779fe6060f1SDimitry Andric       return eKeyHandled;
2780fe6060f1SDimitry Andric     }
2781fe6060f1SDimitry Andric 
2782fe6060f1SDimitry Andric     m_selection_index--;
2783fe6060f1SDimitry Andric     SkipPreviousHiddenFields();
2784fe6060f1SDimitry Andric 
2785fe6060f1SDimitry Andric     if (m_selection_type == SelectionType::Field) {
2786fe6060f1SDimitry Andric       FieldDelegate *previous_field =
2787fe6060f1SDimitry Andric           m_delegate_sp->GetField(m_selection_index);
2788fe6060f1SDimitry Andric       previous_field->FieldDelegateSelectLastElement();
2789fe6060f1SDimitry Andric     }
2790fe6060f1SDimitry Andric 
2791fe6060f1SDimitry Andric     return eKeyHandled;
2792fe6060f1SDimitry Andric   }
2793fe6060f1SDimitry Andric 
2794349cc55cSDimitry Andric   void ExecuteAction(Window &window, int index) {
2795349cc55cSDimitry Andric     FormAction &action = m_delegate_sp->GetAction(index);
2796fe6060f1SDimitry Andric     action.Execute(window);
2797fe6060f1SDimitry Andric     if (m_delegate_sp->HasError()) {
2798fe6060f1SDimitry Andric       m_first_visible_line = 0;
2799fe6060f1SDimitry Andric       m_selection_index = 0;
2800fe6060f1SDimitry Andric       m_selection_type = SelectionType::Field;
2801fe6060f1SDimitry Andric     }
2802fe6060f1SDimitry Andric   }
2803fe6060f1SDimitry Andric 
2804349cc55cSDimitry Andric   // Always return eKeyHandled to absorb all events since forms are always
2805349cc55cSDimitry Andric   // added as pop-ups that should take full control until canceled or submitted.
2806fe6060f1SDimitry Andric   HandleCharResult WindowDelegateHandleChar(Window &window, int key) override {
2807fe6060f1SDimitry Andric     switch (key) {
2808fe6060f1SDimitry Andric     case '\r':
2809fe6060f1SDimitry Andric     case '\n':
2810fe6060f1SDimitry Andric     case KEY_ENTER:
2811fe6060f1SDimitry Andric       if (m_selection_type == SelectionType::Action) {
2812349cc55cSDimitry Andric         ExecuteAction(window, m_selection_index);
2813fe6060f1SDimitry Andric         return eKeyHandled;
2814fe6060f1SDimitry Andric       }
2815fe6060f1SDimitry Andric       break;
2816349cc55cSDimitry Andric     case KEY_ALT_ENTER:
2817349cc55cSDimitry Andric       ExecuteAction(window, 0);
2818349cc55cSDimitry Andric       return eKeyHandled;
2819fe6060f1SDimitry Andric     case '\t':
2820349cc55cSDimitry Andric       SelectNext(key);
2821349cc55cSDimitry Andric       return eKeyHandled;
2822fe6060f1SDimitry Andric     case KEY_SHIFT_TAB:
2823349cc55cSDimitry Andric       SelectPrevious(key);
2824349cc55cSDimitry Andric       return eKeyHandled;
2825fe6060f1SDimitry Andric     case KEY_ESCAPE:
2826fe6060f1SDimitry Andric       window.GetParent()->RemoveSubWindow(&window);
2827fe6060f1SDimitry Andric       return eKeyHandled;
2828fe6060f1SDimitry Andric     default:
2829fe6060f1SDimitry Andric       break;
2830fe6060f1SDimitry Andric     }
2831fe6060f1SDimitry Andric 
2832fe6060f1SDimitry Andric     // If the key wasn't handled and one of the fields is selected, pass the key
2833fe6060f1SDimitry Andric     // to that field.
2834fe6060f1SDimitry Andric     if (m_selection_type == SelectionType::Field) {
2835fe6060f1SDimitry Andric       FieldDelegate *field = m_delegate_sp->GetField(m_selection_index);
2836349cc55cSDimitry Andric       if (field->FieldDelegateHandleChar(key) == eKeyHandled)
2837349cc55cSDimitry Andric         return eKeyHandled;
2838fe6060f1SDimitry Andric     }
2839fe6060f1SDimitry Andric 
2840349cc55cSDimitry Andric     // If the key wasn't handled by the possibly selected field, handle some
2841349cc55cSDimitry Andric     // extra keys for navigation.
2842349cc55cSDimitry Andric     switch (key) {
2843349cc55cSDimitry Andric     case KEY_DOWN:
2844349cc55cSDimitry Andric       SelectNext(key);
2845349cc55cSDimitry Andric       return eKeyHandled;
2846349cc55cSDimitry Andric     case KEY_UP:
2847349cc55cSDimitry Andric       SelectPrevious(key);
2848349cc55cSDimitry Andric       return eKeyHandled;
2849349cc55cSDimitry Andric     default:
2850349cc55cSDimitry Andric       break;
2851349cc55cSDimitry Andric     }
2852349cc55cSDimitry Andric 
2853349cc55cSDimitry Andric     return eKeyHandled;
2854fe6060f1SDimitry Andric   }
2855fe6060f1SDimitry Andric 
2856fe6060f1SDimitry Andric protected:
2857fe6060f1SDimitry Andric   FormDelegateSP m_delegate_sp;
2858fe6060f1SDimitry Andric   // The index of the currently selected SelectionType.
285981ad6265SDimitry Andric   int m_selection_index = 0;
2860fe6060f1SDimitry Andric   // See SelectionType class enum.
2861fe6060f1SDimitry Andric   SelectionType m_selection_type;
2862fe6060f1SDimitry Andric   // The first visible line from the pad.
286381ad6265SDimitry Andric   int m_first_visible_line = 0;
2864fe6060f1SDimitry Andric };
2865fe6060f1SDimitry Andric 
2866fe6060f1SDimitry Andric ///////////////////////////
2867fe6060f1SDimitry Andric // Form Delegate Instances
2868fe6060f1SDimitry Andric ///////////////////////////
2869fe6060f1SDimitry Andric 
2870fe6060f1SDimitry Andric class DetachOrKillProcessFormDelegate : public FormDelegate {
2871fe6060f1SDimitry Andric public:
2872fe6060f1SDimitry Andric   DetachOrKillProcessFormDelegate(Process *process) : m_process(process) {
2873fe6060f1SDimitry Andric     SetError("There is a running process, either detach or kill it.");
2874fe6060f1SDimitry Andric 
2875fe6060f1SDimitry Andric     m_keep_stopped_field =
2876fe6060f1SDimitry Andric         AddBooleanField("Keep process stopped when detaching.", false);
2877fe6060f1SDimitry Andric 
2878fe6060f1SDimitry Andric     AddAction("Detach", [this](Window &window) { Detach(window); });
2879fe6060f1SDimitry Andric     AddAction("Kill", [this](Window &window) { Kill(window); });
2880fe6060f1SDimitry Andric   }
2881fe6060f1SDimitry Andric 
2882fe6060f1SDimitry Andric   std::string GetName() override { return "Detach/Kill Process"; }
2883fe6060f1SDimitry Andric 
2884fe6060f1SDimitry Andric   void Kill(Window &window) {
2885fe6060f1SDimitry Andric     Status destroy_status(m_process->Destroy(false));
2886fe6060f1SDimitry Andric     if (destroy_status.Fail()) {
2887fe6060f1SDimitry Andric       SetError("Failed to kill process.");
2888fe6060f1SDimitry Andric       return;
2889fe6060f1SDimitry Andric     }
2890fe6060f1SDimitry Andric     window.GetParent()->RemoveSubWindow(&window);
2891fe6060f1SDimitry Andric   }
2892fe6060f1SDimitry Andric 
2893fe6060f1SDimitry Andric   void Detach(Window &window) {
2894fe6060f1SDimitry Andric     Status detach_status(m_process->Detach(m_keep_stopped_field->GetBoolean()));
2895fe6060f1SDimitry Andric     if (detach_status.Fail()) {
2896fe6060f1SDimitry Andric       SetError("Failed to detach from process.");
2897fe6060f1SDimitry Andric       return;
2898fe6060f1SDimitry Andric     }
2899fe6060f1SDimitry Andric     window.GetParent()->RemoveSubWindow(&window);
2900fe6060f1SDimitry Andric   }
2901fe6060f1SDimitry Andric 
2902fe6060f1SDimitry Andric protected:
2903fe6060f1SDimitry Andric   Process *m_process;
2904fe6060f1SDimitry Andric   BooleanFieldDelegate *m_keep_stopped_field;
2905fe6060f1SDimitry Andric };
2906fe6060f1SDimitry Andric 
2907fe6060f1SDimitry Andric class ProcessAttachFormDelegate : public FormDelegate {
2908fe6060f1SDimitry Andric public:
2909fe6060f1SDimitry Andric   ProcessAttachFormDelegate(Debugger &debugger, WindowSP main_window_sp)
2910fe6060f1SDimitry Andric       : m_debugger(debugger), m_main_window_sp(main_window_sp) {
2911fe6060f1SDimitry Andric     std::vector<std::string> types;
2912fe6060f1SDimitry Andric     types.push_back(std::string("Name"));
2913fe6060f1SDimitry Andric     types.push_back(std::string("PID"));
2914fe6060f1SDimitry Andric     m_type_field = AddChoicesField("Attach By", 2, types);
2915fe6060f1SDimitry Andric     m_pid_field = AddIntegerField("PID", 0, true);
2916fe6060f1SDimitry Andric     m_name_field =
2917fe6060f1SDimitry Andric         AddTextField("Process Name", GetDefaultProcessName().c_str(), true);
2918fe6060f1SDimitry Andric     m_continue_field = AddBooleanField("Continue once attached.", false);
2919fe6060f1SDimitry Andric     m_wait_for_field = AddBooleanField("Wait for process to launch.", false);
2920fe6060f1SDimitry Andric     m_include_existing_field =
2921fe6060f1SDimitry Andric         AddBooleanField("Include existing processes.", false);
2922fe6060f1SDimitry Andric     m_show_advanced_field = AddBooleanField("Show advanced settings.", false);
2923fe6060f1SDimitry Andric     m_plugin_field = AddProcessPluginField();
2924fe6060f1SDimitry Andric 
2925fe6060f1SDimitry Andric     AddAction("Attach", [this](Window &window) { Attach(window); });
2926fe6060f1SDimitry Andric   }
2927fe6060f1SDimitry Andric 
2928fe6060f1SDimitry Andric   std::string GetName() override { return "Attach Process"; }
2929fe6060f1SDimitry Andric 
2930fe6060f1SDimitry Andric   void UpdateFieldsVisibility() override {
2931fe6060f1SDimitry Andric     if (m_type_field->GetChoiceContent() == "Name") {
2932fe6060f1SDimitry Andric       m_pid_field->FieldDelegateHide();
2933fe6060f1SDimitry Andric       m_name_field->FieldDelegateShow();
2934fe6060f1SDimitry Andric       m_wait_for_field->FieldDelegateShow();
2935fe6060f1SDimitry Andric       if (m_wait_for_field->GetBoolean())
2936fe6060f1SDimitry Andric         m_include_existing_field->FieldDelegateShow();
2937fe6060f1SDimitry Andric       else
2938fe6060f1SDimitry Andric         m_include_existing_field->FieldDelegateHide();
2939fe6060f1SDimitry Andric     } else {
2940fe6060f1SDimitry Andric       m_pid_field->FieldDelegateShow();
2941fe6060f1SDimitry Andric       m_name_field->FieldDelegateHide();
2942fe6060f1SDimitry Andric       m_wait_for_field->FieldDelegateHide();
2943fe6060f1SDimitry Andric       m_include_existing_field->FieldDelegateHide();
2944fe6060f1SDimitry Andric     }
2945fe6060f1SDimitry Andric     if (m_show_advanced_field->GetBoolean())
2946fe6060f1SDimitry Andric       m_plugin_field->FieldDelegateShow();
2947fe6060f1SDimitry Andric     else
2948fe6060f1SDimitry Andric       m_plugin_field->FieldDelegateHide();
2949fe6060f1SDimitry Andric   }
2950fe6060f1SDimitry Andric 
2951fe6060f1SDimitry Andric   // Get the basename of the target's main executable if available, empty string
2952fe6060f1SDimitry Andric   // otherwise.
2953fe6060f1SDimitry Andric   std::string GetDefaultProcessName() {
2954fe6060f1SDimitry Andric     Target *target = m_debugger.GetSelectedTarget().get();
2955fe6060f1SDimitry Andric     if (target == nullptr)
2956fe6060f1SDimitry Andric       return "";
2957fe6060f1SDimitry Andric 
2958fe6060f1SDimitry Andric     ModuleSP module_sp = target->GetExecutableModule();
2959fe6060f1SDimitry Andric     if (!module_sp->IsExecutable())
2960fe6060f1SDimitry Andric       return "";
2961fe6060f1SDimitry Andric 
2962fe6060f1SDimitry Andric     return module_sp->GetFileSpec().GetFilename().AsCString();
2963fe6060f1SDimitry Andric   }
2964fe6060f1SDimitry Andric 
2965fe6060f1SDimitry Andric   bool StopRunningProcess() {
2966fe6060f1SDimitry Andric     ExecutionContext exe_ctx =
2967fe6060f1SDimitry Andric         m_debugger.GetCommandInterpreter().GetExecutionContext();
2968fe6060f1SDimitry Andric 
2969fe6060f1SDimitry Andric     if (!exe_ctx.HasProcessScope())
2970fe6060f1SDimitry Andric       return false;
2971fe6060f1SDimitry Andric 
2972fe6060f1SDimitry Andric     Process *process = exe_ctx.GetProcessPtr();
2973fe6060f1SDimitry Andric     if (!(process && process->IsAlive()))
2974fe6060f1SDimitry Andric       return false;
2975fe6060f1SDimitry Andric 
2976fe6060f1SDimitry Andric     FormDelegateSP form_delegate_sp =
2977fe6060f1SDimitry Andric         FormDelegateSP(new DetachOrKillProcessFormDelegate(process));
2978fe6060f1SDimitry Andric     Rect bounds = m_main_window_sp->GetCenteredRect(85, 8);
2979fe6060f1SDimitry Andric     WindowSP form_window_sp = m_main_window_sp->CreateSubWindow(
2980fe6060f1SDimitry Andric         form_delegate_sp->GetName().c_str(), bounds, true);
2981fe6060f1SDimitry Andric     WindowDelegateSP window_delegate_sp =
2982fe6060f1SDimitry Andric         WindowDelegateSP(new FormWindowDelegate(form_delegate_sp));
2983fe6060f1SDimitry Andric     form_window_sp->SetDelegate(window_delegate_sp);
2984fe6060f1SDimitry Andric 
2985fe6060f1SDimitry Andric     return true;
2986fe6060f1SDimitry Andric   }
2987fe6060f1SDimitry Andric 
2988fe6060f1SDimitry Andric   Target *GetTarget() {
2989fe6060f1SDimitry Andric     Target *target = m_debugger.GetSelectedTarget().get();
2990fe6060f1SDimitry Andric 
2991fe6060f1SDimitry Andric     if (target != nullptr)
2992fe6060f1SDimitry Andric       return target;
2993fe6060f1SDimitry Andric 
2994fe6060f1SDimitry Andric     TargetSP new_target_sp;
2995fe6060f1SDimitry Andric     m_debugger.GetTargetList().CreateTarget(
2996fe6060f1SDimitry Andric         m_debugger, "", "", eLoadDependentsNo, nullptr, new_target_sp);
2997fe6060f1SDimitry Andric 
2998fe6060f1SDimitry Andric     target = new_target_sp.get();
2999fe6060f1SDimitry Andric 
3000fe6060f1SDimitry Andric     if (target == nullptr)
3001fe6060f1SDimitry Andric       SetError("Failed to create target.");
3002fe6060f1SDimitry Andric 
3003fe6060f1SDimitry Andric     m_debugger.GetTargetList().SetSelectedTarget(new_target_sp);
3004fe6060f1SDimitry Andric 
3005fe6060f1SDimitry Andric     return target;
3006fe6060f1SDimitry Andric   }
3007fe6060f1SDimitry Andric 
3008fe6060f1SDimitry Andric   ProcessAttachInfo GetAttachInfo() {
3009fe6060f1SDimitry Andric     ProcessAttachInfo attach_info;
3010fe6060f1SDimitry Andric     attach_info.SetContinueOnceAttached(m_continue_field->GetBoolean());
3011fe6060f1SDimitry Andric     if (m_type_field->GetChoiceContent() == "Name") {
3012fe6060f1SDimitry Andric       attach_info.GetExecutableFile().SetFile(m_name_field->GetText(),
3013fe6060f1SDimitry Andric                                               FileSpec::Style::native);
3014fe6060f1SDimitry Andric       attach_info.SetWaitForLaunch(m_wait_for_field->GetBoolean());
3015fe6060f1SDimitry Andric       if (m_wait_for_field->GetBoolean())
3016fe6060f1SDimitry Andric         attach_info.SetIgnoreExisting(!m_include_existing_field->GetBoolean());
3017fe6060f1SDimitry Andric     } else {
3018fe6060f1SDimitry Andric       attach_info.SetProcessID(m_pid_field->GetInteger());
3019fe6060f1SDimitry Andric     }
3020fe6060f1SDimitry Andric     attach_info.SetProcessPluginName(m_plugin_field->GetPluginName());
3021fe6060f1SDimitry Andric 
3022fe6060f1SDimitry Andric     return attach_info;
3023fe6060f1SDimitry Andric   }
3024fe6060f1SDimitry Andric 
3025fe6060f1SDimitry Andric   void Attach(Window &window) {
3026fe6060f1SDimitry Andric     ClearError();
3027fe6060f1SDimitry Andric 
3028fe6060f1SDimitry Andric     bool all_fields_are_valid = CheckFieldsValidity();
3029fe6060f1SDimitry Andric     if (!all_fields_are_valid)
3030fe6060f1SDimitry Andric       return;
3031fe6060f1SDimitry Andric 
3032fe6060f1SDimitry Andric     bool process_is_running = StopRunningProcess();
3033fe6060f1SDimitry Andric     if (process_is_running)
3034fe6060f1SDimitry Andric       return;
3035fe6060f1SDimitry Andric 
3036fe6060f1SDimitry Andric     Target *target = GetTarget();
3037fe6060f1SDimitry Andric     if (HasError())
3038fe6060f1SDimitry Andric       return;
3039fe6060f1SDimitry Andric 
3040fe6060f1SDimitry Andric     StreamString stream;
3041fe6060f1SDimitry Andric     ProcessAttachInfo attach_info = GetAttachInfo();
3042fe6060f1SDimitry Andric     Status status = target->Attach(attach_info, &stream);
3043fe6060f1SDimitry Andric 
3044fe6060f1SDimitry Andric     if (status.Fail()) {
3045fe6060f1SDimitry Andric       SetError(status.AsCString());
3046fe6060f1SDimitry Andric       return;
3047fe6060f1SDimitry Andric     }
3048fe6060f1SDimitry Andric 
3049fe6060f1SDimitry Andric     ProcessSP process_sp(target->GetProcessSP());
3050fe6060f1SDimitry Andric     if (!process_sp) {
3051fe6060f1SDimitry Andric       SetError("Attached sucessfully but target has no process.");
3052fe6060f1SDimitry Andric       return;
3053fe6060f1SDimitry Andric     }
3054fe6060f1SDimitry Andric 
3055fe6060f1SDimitry Andric     if (attach_info.GetContinueOnceAttached())
3056fe6060f1SDimitry Andric       process_sp->Resume();
3057fe6060f1SDimitry Andric 
3058fe6060f1SDimitry Andric     window.GetParent()->RemoveSubWindow(&window);
3059fe6060f1SDimitry Andric   }
3060fe6060f1SDimitry Andric 
3061fe6060f1SDimitry Andric protected:
3062fe6060f1SDimitry Andric   Debugger &m_debugger;
3063fe6060f1SDimitry Andric   WindowSP m_main_window_sp;
3064fe6060f1SDimitry Andric 
3065fe6060f1SDimitry Andric   ChoicesFieldDelegate *m_type_field;
3066fe6060f1SDimitry Andric   IntegerFieldDelegate *m_pid_field;
3067fe6060f1SDimitry Andric   TextFieldDelegate *m_name_field;
3068fe6060f1SDimitry Andric   BooleanFieldDelegate *m_continue_field;
3069fe6060f1SDimitry Andric   BooleanFieldDelegate *m_wait_for_field;
3070fe6060f1SDimitry Andric   BooleanFieldDelegate *m_include_existing_field;
3071fe6060f1SDimitry Andric   BooleanFieldDelegate *m_show_advanced_field;
3072fe6060f1SDimitry Andric   ProcessPluginFieldDelegate *m_plugin_field;
3073fe6060f1SDimitry Andric };
3074fe6060f1SDimitry Andric 
3075349cc55cSDimitry Andric class TargetCreateFormDelegate : public FormDelegate {
3076349cc55cSDimitry Andric public:
3077349cc55cSDimitry Andric   TargetCreateFormDelegate(Debugger &debugger) : m_debugger(debugger) {
3078349cc55cSDimitry Andric     m_executable_field = AddFileField("Executable", "", /*need_to_exist=*/true,
3079349cc55cSDimitry Andric                                       /*required=*/true);
3080349cc55cSDimitry Andric     m_core_file_field = AddFileField("Core File", "", /*need_to_exist=*/true,
3081349cc55cSDimitry Andric                                      /*required=*/false);
3082349cc55cSDimitry Andric     m_symbol_file_field = AddFileField(
3083349cc55cSDimitry Andric         "Symbol File", "", /*need_to_exist=*/true, /*required=*/false);
3084349cc55cSDimitry Andric     m_show_advanced_field = AddBooleanField("Show advanced settings.", false);
3085349cc55cSDimitry Andric     m_remote_file_field = AddFileField(
3086349cc55cSDimitry Andric         "Remote File", "", /*need_to_exist=*/false, /*required=*/false);
3087349cc55cSDimitry Andric     m_arch_field = AddArchField("Architecture", "", /*required=*/false);
3088349cc55cSDimitry Andric     m_platform_field = AddPlatformPluginField(debugger);
3089349cc55cSDimitry Andric     m_load_dependent_files_field =
3090349cc55cSDimitry Andric         AddChoicesField("Load Dependents", 3, GetLoadDependentFilesChoices());
3091349cc55cSDimitry Andric 
3092349cc55cSDimitry Andric     AddAction("Create", [this](Window &window) { CreateTarget(window); });
3093349cc55cSDimitry Andric   }
3094349cc55cSDimitry Andric 
3095349cc55cSDimitry Andric   std::string GetName() override { return "Create Target"; }
3096349cc55cSDimitry Andric 
3097349cc55cSDimitry Andric   void UpdateFieldsVisibility() override {
3098349cc55cSDimitry Andric     if (m_show_advanced_field->GetBoolean()) {
3099349cc55cSDimitry Andric       m_remote_file_field->FieldDelegateShow();
3100349cc55cSDimitry Andric       m_arch_field->FieldDelegateShow();
3101349cc55cSDimitry Andric       m_platform_field->FieldDelegateShow();
3102349cc55cSDimitry Andric       m_load_dependent_files_field->FieldDelegateShow();
3103349cc55cSDimitry Andric     } else {
3104349cc55cSDimitry Andric       m_remote_file_field->FieldDelegateHide();
3105349cc55cSDimitry Andric       m_arch_field->FieldDelegateHide();
3106349cc55cSDimitry Andric       m_platform_field->FieldDelegateHide();
3107349cc55cSDimitry Andric       m_load_dependent_files_field->FieldDelegateHide();
3108349cc55cSDimitry Andric     }
3109349cc55cSDimitry Andric   }
3110349cc55cSDimitry Andric 
3111349cc55cSDimitry Andric   static constexpr const char *kLoadDependentFilesNo = "No";
3112349cc55cSDimitry Andric   static constexpr const char *kLoadDependentFilesYes = "Yes";
3113349cc55cSDimitry Andric   static constexpr const char *kLoadDependentFilesExecOnly = "Executable only";
3114349cc55cSDimitry Andric 
3115349cc55cSDimitry Andric   std::vector<std::string> GetLoadDependentFilesChoices() {
3116bdd1243dSDimitry Andric     std::vector<std::string> load_dependents_options;
3117bdd1243dSDimitry Andric     load_dependents_options.push_back(kLoadDependentFilesExecOnly);
3118bdd1243dSDimitry Andric     load_dependents_options.push_back(kLoadDependentFilesYes);
3119bdd1243dSDimitry Andric     load_dependents_options.push_back(kLoadDependentFilesNo);
3120bdd1243dSDimitry Andric     return load_dependents_options;
3121349cc55cSDimitry Andric   }
3122349cc55cSDimitry Andric 
3123349cc55cSDimitry Andric   LoadDependentFiles GetLoadDependentFiles() {
3124349cc55cSDimitry Andric     std::string choice = m_load_dependent_files_field->GetChoiceContent();
3125349cc55cSDimitry Andric     if (choice == kLoadDependentFilesNo)
3126349cc55cSDimitry Andric       return eLoadDependentsNo;
3127349cc55cSDimitry Andric     if (choice == kLoadDependentFilesYes)
3128349cc55cSDimitry Andric       return eLoadDependentsYes;
3129349cc55cSDimitry Andric     return eLoadDependentsDefault;
3130349cc55cSDimitry Andric   }
3131349cc55cSDimitry Andric 
3132349cc55cSDimitry Andric   OptionGroupPlatform GetPlatformOptions() {
3133349cc55cSDimitry Andric     OptionGroupPlatform platform_options(false);
3134349cc55cSDimitry Andric     platform_options.SetPlatformName(m_platform_field->GetPluginName().c_str());
3135349cc55cSDimitry Andric     return platform_options;
3136349cc55cSDimitry Andric   }
3137349cc55cSDimitry Andric 
3138349cc55cSDimitry Andric   TargetSP GetTarget() {
3139349cc55cSDimitry Andric     OptionGroupPlatform platform_options = GetPlatformOptions();
3140349cc55cSDimitry Andric     TargetSP target_sp;
3141349cc55cSDimitry Andric     Status status = m_debugger.GetTargetList().CreateTarget(
3142349cc55cSDimitry Andric         m_debugger, m_executable_field->GetPath(),
3143349cc55cSDimitry Andric         m_arch_field->GetArchString(), GetLoadDependentFiles(),
3144349cc55cSDimitry Andric         &platform_options, target_sp);
3145349cc55cSDimitry Andric 
3146349cc55cSDimitry Andric     if (status.Fail()) {
3147349cc55cSDimitry Andric       SetError(status.AsCString());
3148349cc55cSDimitry Andric       return nullptr;
3149349cc55cSDimitry Andric     }
3150349cc55cSDimitry Andric 
3151349cc55cSDimitry Andric     m_debugger.GetTargetList().SetSelectedTarget(target_sp);
3152349cc55cSDimitry Andric 
3153349cc55cSDimitry Andric     return target_sp;
3154349cc55cSDimitry Andric   }
3155349cc55cSDimitry Andric 
3156349cc55cSDimitry Andric   void SetSymbolFile(TargetSP target_sp) {
3157349cc55cSDimitry Andric     if (!m_symbol_file_field->IsSpecified())
3158349cc55cSDimitry Andric       return;
3159349cc55cSDimitry Andric 
3160349cc55cSDimitry Andric     ModuleSP module_sp(target_sp->GetExecutableModule());
3161349cc55cSDimitry Andric     if (!module_sp)
3162349cc55cSDimitry Andric       return;
3163349cc55cSDimitry Andric 
3164349cc55cSDimitry Andric     module_sp->SetSymbolFileFileSpec(
3165349cc55cSDimitry Andric         m_symbol_file_field->GetResolvedFileSpec());
3166349cc55cSDimitry Andric   }
3167349cc55cSDimitry Andric 
3168349cc55cSDimitry Andric   void SetCoreFile(TargetSP target_sp) {
3169349cc55cSDimitry Andric     if (!m_core_file_field->IsSpecified())
3170349cc55cSDimitry Andric       return;
3171349cc55cSDimitry Andric 
3172349cc55cSDimitry Andric     FileSpec core_file_spec = m_core_file_field->GetResolvedFileSpec();
3173349cc55cSDimitry Andric 
3174349cc55cSDimitry Andric     FileSpec core_file_directory_spec;
3175bdd1243dSDimitry Andric     core_file_directory_spec.SetDirectory(core_file_spec.GetDirectory());
3176349cc55cSDimitry Andric     target_sp->AppendExecutableSearchPaths(core_file_directory_spec);
3177349cc55cSDimitry Andric 
3178349cc55cSDimitry Andric     ProcessSP process_sp(target_sp->CreateProcess(
3179349cc55cSDimitry Andric         m_debugger.GetListener(), llvm::StringRef(), &core_file_spec, false));
3180349cc55cSDimitry Andric 
3181349cc55cSDimitry Andric     if (!process_sp) {
3182349cc55cSDimitry Andric       SetError("Unable to find process plug-in for core file!");
3183349cc55cSDimitry Andric       return;
3184349cc55cSDimitry Andric     }
3185349cc55cSDimitry Andric 
3186349cc55cSDimitry Andric     Status status = process_sp->LoadCore();
3187349cc55cSDimitry Andric     if (status.Fail()) {
3188349cc55cSDimitry Andric       SetError("Can't find plug-in for core file!");
3189349cc55cSDimitry Andric       return;
3190349cc55cSDimitry Andric     }
3191349cc55cSDimitry Andric   }
3192349cc55cSDimitry Andric 
3193349cc55cSDimitry Andric   void SetRemoteFile(TargetSP target_sp) {
3194349cc55cSDimitry Andric     if (!m_remote_file_field->IsSpecified())
3195349cc55cSDimitry Andric       return;
3196349cc55cSDimitry Andric 
3197349cc55cSDimitry Andric     ModuleSP module_sp(target_sp->GetExecutableModule());
3198349cc55cSDimitry Andric     if (!module_sp)
3199349cc55cSDimitry Andric       return;
3200349cc55cSDimitry Andric 
3201349cc55cSDimitry Andric     FileSpec remote_file_spec = m_remote_file_field->GetFileSpec();
3202349cc55cSDimitry Andric     module_sp->SetPlatformFileSpec(remote_file_spec);
3203349cc55cSDimitry Andric   }
3204349cc55cSDimitry Andric 
3205349cc55cSDimitry Andric   void RemoveTarget(TargetSP target_sp) {
3206349cc55cSDimitry Andric     m_debugger.GetTargetList().DeleteTarget(target_sp);
3207349cc55cSDimitry Andric   }
3208349cc55cSDimitry Andric 
3209349cc55cSDimitry Andric   void CreateTarget(Window &window) {
3210349cc55cSDimitry Andric     ClearError();
3211349cc55cSDimitry Andric 
3212349cc55cSDimitry Andric     bool all_fields_are_valid = CheckFieldsValidity();
3213349cc55cSDimitry Andric     if (!all_fields_are_valid)
3214349cc55cSDimitry Andric       return;
3215349cc55cSDimitry Andric 
3216349cc55cSDimitry Andric     TargetSP target_sp = GetTarget();
3217349cc55cSDimitry Andric     if (HasError())
3218349cc55cSDimitry Andric       return;
3219349cc55cSDimitry Andric 
3220349cc55cSDimitry Andric     SetSymbolFile(target_sp);
3221349cc55cSDimitry Andric     if (HasError()) {
3222349cc55cSDimitry Andric       RemoveTarget(target_sp);
3223349cc55cSDimitry Andric       return;
3224349cc55cSDimitry Andric     }
3225349cc55cSDimitry Andric 
3226349cc55cSDimitry Andric     SetCoreFile(target_sp);
3227349cc55cSDimitry Andric     if (HasError()) {
3228349cc55cSDimitry Andric       RemoveTarget(target_sp);
3229349cc55cSDimitry Andric       return;
3230349cc55cSDimitry Andric     }
3231349cc55cSDimitry Andric 
3232349cc55cSDimitry Andric     SetRemoteFile(target_sp);
3233349cc55cSDimitry Andric     if (HasError()) {
3234349cc55cSDimitry Andric       RemoveTarget(target_sp);
3235349cc55cSDimitry Andric       return;
3236349cc55cSDimitry Andric     }
3237349cc55cSDimitry Andric 
3238349cc55cSDimitry Andric     window.GetParent()->RemoveSubWindow(&window);
3239349cc55cSDimitry Andric   }
3240349cc55cSDimitry Andric 
3241349cc55cSDimitry Andric protected:
3242349cc55cSDimitry Andric   Debugger &m_debugger;
3243349cc55cSDimitry Andric 
3244349cc55cSDimitry Andric   FileFieldDelegate *m_executable_field;
3245349cc55cSDimitry Andric   FileFieldDelegate *m_core_file_field;
3246349cc55cSDimitry Andric   FileFieldDelegate *m_symbol_file_field;
3247349cc55cSDimitry Andric   BooleanFieldDelegate *m_show_advanced_field;
3248349cc55cSDimitry Andric   FileFieldDelegate *m_remote_file_field;
3249349cc55cSDimitry Andric   ArchFieldDelegate *m_arch_field;
3250349cc55cSDimitry Andric   PlatformPluginFieldDelegate *m_platform_field;
3251349cc55cSDimitry Andric   ChoicesFieldDelegate *m_load_dependent_files_field;
3252349cc55cSDimitry Andric };
3253349cc55cSDimitry Andric 
3254349cc55cSDimitry Andric class ProcessLaunchFormDelegate : public FormDelegate {
3255349cc55cSDimitry Andric public:
3256349cc55cSDimitry Andric   ProcessLaunchFormDelegate(Debugger &debugger, WindowSP main_window_sp)
3257349cc55cSDimitry Andric       : m_debugger(debugger), m_main_window_sp(main_window_sp) {
3258349cc55cSDimitry Andric 
3259349cc55cSDimitry Andric     m_arguments_field = AddArgumentsField();
3260349cc55cSDimitry Andric     SetArgumentsFieldDefaultValue();
3261349cc55cSDimitry Andric     m_target_environment_field =
3262349cc55cSDimitry Andric         AddEnvironmentVariableListField("Target Environment Variables");
3263349cc55cSDimitry Andric     SetTargetEnvironmentFieldDefaultValue();
3264349cc55cSDimitry Andric     m_working_directory_field = AddDirectoryField(
3265349cc55cSDimitry Andric         "Working Directory", GetDefaultWorkingDirectory().c_str(), true, false);
3266349cc55cSDimitry Andric 
3267349cc55cSDimitry Andric     m_show_advanced_field = AddBooleanField("Show advanced settings.", false);
3268349cc55cSDimitry Andric 
3269349cc55cSDimitry Andric     m_stop_at_entry_field = AddBooleanField("Stop at entry point.", false);
3270349cc55cSDimitry Andric     m_detach_on_error_field =
3271349cc55cSDimitry Andric         AddBooleanField("Detach on error.", GetDefaultDetachOnError());
3272349cc55cSDimitry Andric     m_disable_aslr_field =
3273349cc55cSDimitry Andric         AddBooleanField("Disable ASLR", GetDefaultDisableASLR());
3274349cc55cSDimitry Andric     m_plugin_field = AddProcessPluginField();
3275349cc55cSDimitry Andric     m_arch_field = AddArchField("Architecture", "", false);
3276349cc55cSDimitry Andric     m_shell_field = AddFileField("Shell", "", true, false);
3277349cc55cSDimitry Andric     m_expand_shell_arguments_field =
3278349cc55cSDimitry Andric         AddBooleanField("Expand shell arguments.", false);
3279349cc55cSDimitry Andric 
3280349cc55cSDimitry Andric     m_disable_standard_io_field =
3281349cc55cSDimitry Andric         AddBooleanField("Disable Standard IO", GetDefaultDisableStandardIO());
3282349cc55cSDimitry Andric     m_standard_output_field =
3283349cc55cSDimitry Andric         AddFileField("Standard Output File", "", /*need_to_exist=*/false,
3284349cc55cSDimitry Andric                      /*required=*/false);
3285349cc55cSDimitry Andric     m_standard_error_field =
3286349cc55cSDimitry Andric         AddFileField("Standard Error File", "", /*need_to_exist=*/false,
3287349cc55cSDimitry Andric                      /*required=*/false);
3288349cc55cSDimitry Andric     m_standard_input_field =
3289349cc55cSDimitry Andric         AddFileField("Standard Input File", "", /*need_to_exist=*/false,
3290349cc55cSDimitry Andric                      /*required=*/false);
3291349cc55cSDimitry Andric 
3292349cc55cSDimitry Andric     m_show_inherited_environment_field =
3293349cc55cSDimitry Andric         AddBooleanField("Show inherited environment variables.", false);
3294349cc55cSDimitry Andric     m_inherited_environment_field =
3295349cc55cSDimitry Andric         AddEnvironmentVariableListField("Inherited Environment Variables");
3296349cc55cSDimitry Andric     SetInheritedEnvironmentFieldDefaultValue();
3297349cc55cSDimitry Andric 
3298349cc55cSDimitry Andric     AddAction("Launch", [this](Window &window) { Launch(window); });
3299349cc55cSDimitry Andric   }
3300349cc55cSDimitry Andric 
3301349cc55cSDimitry Andric   std::string GetName() override { return "Launch Process"; }
3302349cc55cSDimitry Andric 
3303349cc55cSDimitry Andric   void UpdateFieldsVisibility() override {
3304349cc55cSDimitry Andric     if (m_show_advanced_field->GetBoolean()) {
3305349cc55cSDimitry Andric       m_stop_at_entry_field->FieldDelegateShow();
3306349cc55cSDimitry Andric       m_detach_on_error_field->FieldDelegateShow();
3307349cc55cSDimitry Andric       m_disable_aslr_field->FieldDelegateShow();
3308349cc55cSDimitry Andric       m_plugin_field->FieldDelegateShow();
3309349cc55cSDimitry Andric       m_arch_field->FieldDelegateShow();
3310349cc55cSDimitry Andric       m_shell_field->FieldDelegateShow();
3311349cc55cSDimitry Andric       m_expand_shell_arguments_field->FieldDelegateShow();
3312349cc55cSDimitry Andric       m_disable_standard_io_field->FieldDelegateShow();
3313349cc55cSDimitry Andric       if (m_disable_standard_io_field->GetBoolean()) {
3314349cc55cSDimitry Andric         m_standard_input_field->FieldDelegateHide();
3315349cc55cSDimitry Andric         m_standard_output_field->FieldDelegateHide();
3316349cc55cSDimitry Andric         m_standard_error_field->FieldDelegateHide();
3317349cc55cSDimitry Andric       } else {
3318349cc55cSDimitry Andric         m_standard_input_field->FieldDelegateShow();
3319349cc55cSDimitry Andric         m_standard_output_field->FieldDelegateShow();
3320349cc55cSDimitry Andric         m_standard_error_field->FieldDelegateShow();
3321349cc55cSDimitry Andric       }
3322349cc55cSDimitry Andric       m_show_inherited_environment_field->FieldDelegateShow();
3323349cc55cSDimitry Andric       if (m_show_inherited_environment_field->GetBoolean())
3324349cc55cSDimitry Andric         m_inherited_environment_field->FieldDelegateShow();
3325349cc55cSDimitry Andric       else
3326349cc55cSDimitry Andric         m_inherited_environment_field->FieldDelegateHide();
3327349cc55cSDimitry Andric     } else {
3328349cc55cSDimitry Andric       m_stop_at_entry_field->FieldDelegateHide();
3329349cc55cSDimitry Andric       m_detach_on_error_field->FieldDelegateHide();
3330349cc55cSDimitry Andric       m_disable_aslr_field->FieldDelegateHide();
3331349cc55cSDimitry Andric       m_plugin_field->FieldDelegateHide();
3332349cc55cSDimitry Andric       m_arch_field->FieldDelegateHide();
3333349cc55cSDimitry Andric       m_shell_field->FieldDelegateHide();
3334349cc55cSDimitry Andric       m_expand_shell_arguments_field->FieldDelegateHide();
3335349cc55cSDimitry Andric       m_disable_standard_io_field->FieldDelegateHide();
3336349cc55cSDimitry Andric       m_standard_input_field->FieldDelegateHide();
3337349cc55cSDimitry Andric       m_standard_output_field->FieldDelegateHide();
3338349cc55cSDimitry Andric       m_standard_error_field->FieldDelegateHide();
3339349cc55cSDimitry Andric       m_show_inherited_environment_field->FieldDelegateHide();
3340349cc55cSDimitry Andric       m_inherited_environment_field->FieldDelegateHide();
3341349cc55cSDimitry Andric     }
3342349cc55cSDimitry Andric   }
3343349cc55cSDimitry Andric 
3344349cc55cSDimitry Andric   // Methods for setting the default value of the fields.
3345349cc55cSDimitry Andric 
3346349cc55cSDimitry Andric   void SetArgumentsFieldDefaultValue() {
3347349cc55cSDimitry Andric     TargetSP target = m_debugger.GetSelectedTarget();
3348349cc55cSDimitry Andric     if (target == nullptr)
3349349cc55cSDimitry Andric       return;
3350349cc55cSDimitry Andric 
3351349cc55cSDimitry Andric     const Args &target_arguments =
3352349cc55cSDimitry Andric         target->GetProcessLaunchInfo().GetArguments();
3353349cc55cSDimitry Andric     m_arguments_field->AddArguments(target_arguments);
3354349cc55cSDimitry Andric   }
3355349cc55cSDimitry Andric 
3356349cc55cSDimitry Andric   void SetTargetEnvironmentFieldDefaultValue() {
3357349cc55cSDimitry Andric     TargetSP target = m_debugger.GetSelectedTarget();
3358349cc55cSDimitry Andric     if (target == nullptr)
3359349cc55cSDimitry Andric       return;
3360349cc55cSDimitry Andric 
3361349cc55cSDimitry Andric     const Environment &target_environment = target->GetTargetEnvironment();
3362349cc55cSDimitry Andric     m_target_environment_field->AddEnvironmentVariables(target_environment);
3363349cc55cSDimitry Andric   }
3364349cc55cSDimitry Andric 
3365349cc55cSDimitry Andric   void SetInheritedEnvironmentFieldDefaultValue() {
3366349cc55cSDimitry Andric     TargetSP target = m_debugger.GetSelectedTarget();
3367349cc55cSDimitry Andric     if (target == nullptr)
3368349cc55cSDimitry Andric       return;
3369349cc55cSDimitry Andric 
3370349cc55cSDimitry Andric     const Environment &inherited_environment =
3371349cc55cSDimitry Andric         target->GetInheritedEnvironment();
3372349cc55cSDimitry Andric     m_inherited_environment_field->AddEnvironmentVariables(
3373349cc55cSDimitry Andric         inherited_environment);
3374349cc55cSDimitry Andric   }
3375349cc55cSDimitry Andric 
3376349cc55cSDimitry Andric   std::string GetDefaultWorkingDirectory() {
3377349cc55cSDimitry Andric     TargetSP target = m_debugger.GetSelectedTarget();
3378349cc55cSDimitry Andric     if (target == nullptr)
3379349cc55cSDimitry Andric       return "";
3380349cc55cSDimitry Andric 
3381349cc55cSDimitry Andric     PlatformSP platform = target->GetPlatform();
3382349cc55cSDimitry Andric     return platform->GetWorkingDirectory().GetPath();
3383349cc55cSDimitry Andric   }
3384349cc55cSDimitry Andric 
3385349cc55cSDimitry Andric   bool GetDefaultDisableASLR() {
3386349cc55cSDimitry Andric     TargetSP target = m_debugger.GetSelectedTarget();
3387349cc55cSDimitry Andric     if (target == nullptr)
3388349cc55cSDimitry Andric       return false;
3389349cc55cSDimitry Andric 
3390349cc55cSDimitry Andric     return target->GetDisableASLR();
3391349cc55cSDimitry Andric   }
3392349cc55cSDimitry Andric 
3393349cc55cSDimitry Andric   bool GetDefaultDisableStandardIO() {
3394349cc55cSDimitry Andric     TargetSP target = m_debugger.GetSelectedTarget();
3395349cc55cSDimitry Andric     if (target == nullptr)
3396349cc55cSDimitry Andric       return true;
3397349cc55cSDimitry Andric 
3398349cc55cSDimitry Andric     return target->GetDisableSTDIO();
3399349cc55cSDimitry Andric   }
3400349cc55cSDimitry Andric 
3401349cc55cSDimitry Andric   bool GetDefaultDetachOnError() {
3402349cc55cSDimitry Andric     TargetSP target = m_debugger.GetSelectedTarget();
3403349cc55cSDimitry Andric     if (target == nullptr)
3404349cc55cSDimitry Andric       return true;
3405349cc55cSDimitry Andric 
3406349cc55cSDimitry Andric     return target->GetDetachOnError();
3407349cc55cSDimitry Andric   }
3408349cc55cSDimitry Andric 
3409349cc55cSDimitry Andric   // Methods for getting the necessary information and setting them to the
3410349cc55cSDimitry Andric   // ProcessLaunchInfo.
3411349cc55cSDimitry Andric 
3412349cc55cSDimitry Andric   void GetExecutableSettings(ProcessLaunchInfo &launch_info) {
3413349cc55cSDimitry Andric     TargetSP target = m_debugger.GetSelectedTarget();
3414349cc55cSDimitry Andric     ModuleSP executable_module = target->GetExecutableModule();
3415349cc55cSDimitry Andric     llvm::StringRef target_settings_argv0 = target->GetArg0();
3416349cc55cSDimitry Andric 
3417349cc55cSDimitry Andric     if (!target_settings_argv0.empty()) {
3418349cc55cSDimitry Andric       launch_info.GetArguments().AppendArgument(target_settings_argv0);
3419349cc55cSDimitry Andric       launch_info.SetExecutableFile(executable_module->GetPlatformFileSpec(),
3420349cc55cSDimitry Andric                                     false);
3421349cc55cSDimitry Andric       return;
3422349cc55cSDimitry Andric     }
3423349cc55cSDimitry Andric 
3424349cc55cSDimitry Andric     launch_info.SetExecutableFile(executable_module->GetPlatformFileSpec(),
3425349cc55cSDimitry Andric                                   true);
3426349cc55cSDimitry Andric   }
3427349cc55cSDimitry Andric 
3428349cc55cSDimitry Andric   void GetArguments(ProcessLaunchInfo &launch_info) {
3429349cc55cSDimitry Andric     TargetSP target = m_debugger.GetSelectedTarget();
3430349cc55cSDimitry Andric     Args arguments = m_arguments_field->GetArguments();
3431349cc55cSDimitry Andric     launch_info.GetArguments().AppendArguments(arguments);
3432349cc55cSDimitry Andric   }
3433349cc55cSDimitry Andric 
3434349cc55cSDimitry Andric   void GetEnvironment(ProcessLaunchInfo &launch_info) {
3435349cc55cSDimitry Andric     Environment target_environment =
3436349cc55cSDimitry Andric         m_target_environment_field->GetEnvironment();
3437349cc55cSDimitry Andric     Environment inherited_environment =
3438349cc55cSDimitry Andric         m_inherited_environment_field->GetEnvironment();
3439349cc55cSDimitry Andric     launch_info.GetEnvironment().insert(target_environment.begin(),
3440349cc55cSDimitry Andric                                         target_environment.end());
3441349cc55cSDimitry Andric     launch_info.GetEnvironment().insert(inherited_environment.begin(),
3442349cc55cSDimitry Andric                                         inherited_environment.end());
3443349cc55cSDimitry Andric   }
3444349cc55cSDimitry Andric 
3445349cc55cSDimitry Andric   void GetWorkingDirectory(ProcessLaunchInfo &launch_info) {
3446349cc55cSDimitry Andric     if (m_working_directory_field->IsSpecified())
3447349cc55cSDimitry Andric       launch_info.SetWorkingDirectory(
3448349cc55cSDimitry Andric           m_working_directory_field->GetResolvedFileSpec());
3449349cc55cSDimitry Andric   }
3450349cc55cSDimitry Andric 
3451349cc55cSDimitry Andric   void GetStopAtEntry(ProcessLaunchInfo &launch_info) {
3452349cc55cSDimitry Andric     if (m_stop_at_entry_field->GetBoolean())
3453349cc55cSDimitry Andric       launch_info.GetFlags().Set(eLaunchFlagStopAtEntry);
3454349cc55cSDimitry Andric     else
3455349cc55cSDimitry Andric       launch_info.GetFlags().Clear(eLaunchFlagStopAtEntry);
3456349cc55cSDimitry Andric   }
3457349cc55cSDimitry Andric 
3458349cc55cSDimitry Andric   void GetDetachOnError(ProcessLaunchInfo &launch_info) {
3459349cc55cSDimitry Andric     if (m_detach_on_error_field->GetBoolean())
3460349cc55cSDimitry Andric       launch_info.GetFlags().Set(eLaunchFlagDetachOnError);
3461349cc55cSDimitry Andric     else
3462349cc55cSDimitry Andric       launch_info.GetFlags().Clear(eLaunchFlagDetachOnError);
3463349cc55cSDimitry Andric   }
3464349cc55cSDimitry Andric 
3465349cc55cSDimitry Andric   void GetDisableASLR(ProcessLaunchInfo &launch_info) {
3466349cc55cSDimitry Andric     if (m_disable_aslr_field->GetBoolean())
3467349cc55cSDimitry Andric       launch_info.GetFlags().Set(eLaunchFlagDisableASLR);
3468349cc55cSDimitry Andric     else
3469349cc55cSDimitry Andric       launch_info.GetFlags().Clear(eLaunchFlagDisableASLR);
3470349cc55cSDimitry Andric   }
3471349cc55cSDimitry Andric 
3472349cc55cSDimitry Andric   void GetPlugin(ProcessLaunchInfo &launch_info) {
3473349cc55cSDimitry Andric     launch_info.SetProcessPluginName(m_plugin_field->GetPluginName());
3474349cc55cSDimitry Andric   }
3475349cc55cSDimitry Andric 
3476349cc55cSDimitry Andric   void GetArch(ProcessLaunchInfo &launch_info) {
3477349cc55cSDimitry Andric     if (!m_arch_field->IsSpecified())
3478349cc55cSDimitry Andric       return;
3479349cc55cSDimitry Andric 
3480349cc55cSDimitry Andric     TargetSP target_sp = m_debugger.GetSelectedTarget();
3481349cc55cSDimitry Andric     PlatformSP platform_sp =
3482349cc55cSDimitry Andric         target_sp ? target_sp->GetPlatform() : PlatformSP();
3483349cc55cSDimitry Andric     launch_info.GetArchitecture() = Platform::GetAugmentedArchSpec(
3484349cc55cSDimitry Andric         platform_sp.get(), m_arch_field->GetArchString());
3485349cc55cSDimitry Andric   }
3486349cc55cSDimitry Andric 
3487349cc55cSDimitry Andric   void GetShell(ProcessLaunchInfo &launch_info) {
3488349cc55cSDimitry Andric     if (!m_shell_field->IsSpecified())
3489349cc55cSDimitry Andric       return;
3490349cc55cSDimitry Andric 
3491349cc55cSDimitry Andric     launch_info.SetShell(m_shell_field->GetResolvedFileSpec());
3492349cc55cSDimitry Andric     launch_info.SetShellExpandArguments(
3493349cc55cSDimitry Andric         m_expand_shell_arguments_field->GetBoolean());
3494349cc55cSDimitry Andric   }
3495349cc55cSDimitry Andric 
3496349cc55cSDimitry Andric   void GetStandardIO(ProcessLaunchInfo &launch_info) {
3497349cc55cSDimitry Andric     if (m_disable_standard_io_field->GetBoolean()) {
3498349cc55cSDimitry Andric       launch_info.GetFlags().Set(eLaunchFlagDisableSTDIO);
3499349cc55cSDimitry Andric       return;
3500349cc55cSDimitry Andric     }
3501349cc55cSDimitry Andric 
3502349cc55cSDimitry Andric     FileAction action;
3503349cc55cSDimitry Andric     if (m_standard_input_field->IsSpecified()) {
3504bdd1243dSDimitry Andric       if (action.Open(STDIN_FILENO, m_standard_input_field->GetFileSpec(), true,
3505bdd1243dSDimitry Andric                       false))
3506349cc55cSDimitry Andric         launch_info.AppendFileAction(action);
3507349cc55cSDimitry Andric     }
3508349cc55cSDimitry Andric     if (m_standard_output_field->IsSpecified()) {
3509bdd1243dSDimitry Andric       if (action.Open(STDOUT_FILENO, m_standard_output_field->GetFileSpec(),
3510bdd1243dSDimitry Andric                       false, true))
3511349cc55cSDimitry Andric         launch_info.AppendFileAction(action);
3512349cc55cSDimitry Andric     }
3513349cc55cSDimitry Andric     if (m_standard_error_field->IsSpecified()) {
3514bdd1243dSDimitry Andric       if (action.Open(STDERR_FILENO, m_standard_error_field->GetFileSpec(),
3515bdd1243dSDimitry Andric                       false, true))
3516349cc55cSDimitry Andric         launch_info.AppendFileAction(action);
3517349cc55cSDimitry Andric     }
3518349cc55cSDimitry Andric   }
3519349cc55cSDimitry Andric 
3520349cc55cSDimitry Andric   void GetInheritTCC(ProcessLaunchInfo &launch_info) {
3521349cc55cSDimitry Andric     if (m_debugger.GetSelectedTarget()->GetInheritTCC())
3522349cc55cSDimitry Andric       launch_info.GetFlags().Set(eLaunchFlagInheritTCCFromParent);
3523349cc55cSDimitry Andric   }
3524349cc55cSDimitry Andric 
3525349cc55cSDimitry Andric   ProcessLaunchInfo GetLaunchInfo() {
3526349cc55cSDimitry Andric     ProcessLaunchInfo launch_info;
3527349cc55cSDimitry Andric 
3528349cc55cSDimitry Andric     GetExecutableSettings(launch_info);
3529349cc55cSDimitry Andric     GetArguments(launch_info);
3530349cc55cSDimitry Andric     GetEnvironment(launch_info);
3531349cc55cSDimitry Andric     GetWorkingDirectory(launch_info);
3532349cc55cSDimitry Andric     GetStopAtEntry(launch_info);
3533349cc55cSDimitry Andric     GetDetachOnError(launch_info);
3534349cc55cSDimitry Andric     GetDisableASLR(launch_info);
3535349cc55cSDimitry Andric     GetPlugin(launch_info);
3536349cc55cSDimitry Andric     GetArch(launch_info);
3537349cc55cSDimitry Andric     GetShell(launch_info);
3538349cc55cSDimitry Andric     GetStandardIO(launch_info);
3539349cc55cSDimitry Andric     GetInheritTCC(launch_info);
3540349cc55cSDimitry Andric 
3541349cc55cSDimitry Andric     return launch_info;
3542349cc55cSDimitry Andric   }
3543349cc55cSDimitry Andric 
3544349cc55cSDimitry Andric   bool StopRunningProcess() {
3545349cc55cSDimitry Andric     ExecutionContext exe_ctx =
3546349cc55cSDimitry Andric         m_debugger.GetCommandInterpreter().GetExecutionContext();
3547349cc55cSDimitry Andric 
3548349cc55cSDimitry Andric     if (!exe_ctx.HasProcessScope())
3549349cc55cSDimitry Andric       return false;
3550349cc55cSDimitry Andric 
3551349cc55cSDimitry Andric     Process *process = exe_ctx.GetProcessPtr();
3552349cc55cSDimitry Andric     if (!(process && process->IsAlive()))
3553349cc55cSDimitry Andric       return false;
3554349cc55cSDimitry Andric 
3555349cc55cSDimitry Andric     FormDelegateSP form_delegate_sp =
3556349cc55cSDimitry Andric         FormDelegateSP(new DetachOrKillProcessFormDelegate(process));
3557349cc55cSDimitry Andric     Rect bounds = m_main_window_sp->GetCenteredRect(85, 8);
3558349cc55cSDimitry Andric     WindowSP form_window_sp = m_main_window_sp->CreateSubWindow(
3559349cc55cSDimitry Andric         form_delegate_sp->GetName().c_str(), bounds, true);
3560349cc55cSDimitry Andric     WindowDelegateSP window_delegate_sp =
3561349cc55cSDimitry Andric         WindowDelegateSP(new FormWindowDelegate(form_delegate_sp));
3562349cc55cSDimitry Andric     form_window_sp->SetDelegate(window_delegate_sp);
3563349cc55cSDimitry Andric 
3564349cc55cSDimitry Andric     return true;
3565349cc55cSDimitry Andric   }
3566349cc55cSDimitry Andric 
3567349cc55cSDimitry Andric   Target *GetTarget() {
3568349cc55cSDimitry Andric     Target *target = m_debugger.GetSelectedTarget().get();
3569349cc55cSDimitry Andric 
3570349cc55cSDimitry Andric     if (target == nullptr) {
3571349cc55cSDimitry Andric       SetError("No target exists!");
3572349cc55cSDimitry Andric       return nullptr;
3573349cc55cSDimitry Andric     }
3574349cc55cSDimitry Andric 
3575349cc55cSDimitry Andric     ModuleSP exe_module_sp = target->GetExecutableModule();
3576349cc55cSDimitry Andric 
3577349cc55cSDimitry Andric     if (exe_module_sp == nullptr) {
3578349cc55cSDimitry Andric       SetError("No executable in target!");
3579349cc55cSDimitry Andric       return nullptr;
3580349cc55cSDimitry Andric     }
3581349cc55cSDimitry Andric 
3582349cc55cSDimitry Andric     return target;
3583349cc55cSDimitry Andric   }
3584349cc55cSDimitry Andric 
3585349cc55cSDimitry Andric   void Launch(Window &window) {
3586349cc55cSDimitry Andric     ClearError();
3587349cc55cSDimitry Andric 
3588349cc55cSDimitry Andric     bool all_fields_are_valid = CheckFieldsValidity();
3589349cc55cSDimitry Andric     if (!all_fields_are_valid)
3590349cc55cSDimitry Andric       return;
3591349cc55cSDimitry Andric 
3592349cc55cSDimitry Andric     bool process_is_running = StopRunningProcess();
3593349cc55cSDimitry Andric     if (process_is_running)
3594349cc55cSDimitry Andric       return;
3595349cc55cSDimitry Andric 
3596349cc55cSDimitry Andric     Target *target = GetTarget();
3597349cc55cSDimitry Andric     if (HasError())
3598349cc55cSDimitry Andric       return;
3599349cc55cSDimitry Andric 
3600349cc55cSDimitry Andric     StreamString stream;
3601349cc55cSDimitry Andric     ProcessLaunchInfo launch_info = GetLaunchInfo();
3602349cc55cSDimitry Andric     Status status = target->Launch(launch_info, &stream);
3603349cc55cSDimitry Andric 
3604349cc55cSDimitry Andric     if (status.Fail()) {
3605349cc55cSDimitry Andric       SetError(status.AsCString());
3606349cc55cSDimitry Andric       return;
3607349cc55cSDimitry Andric     }
3608349cc55cSDimitry Andric 
3609349cc55cSDimitry Andric     ProcessSP process_sp(target->GetProcessSP());
3610349cc55cSDimitry Andric     if (!process_sp) {
3611349cc55cSDimitry Andric       SetError("Launched successfully but target has no process!");
3612349cc55cSDimitry Andric       return;
3613349cc55cSDimitry Andric     }
3614349cc55cSDimitry Andric 
3615349cc55cSDimitry Andric     window.GetParent()->RemoveSubWindow(&window);
3616349cc55cSDimitry Andric   }
3617349cc55cSDimitry Andric 
3618349cc55cSDimitry Andric protected:
3619349cc55cSDimitry Andric   Debugger &m_debugger;
3620349cc55cSDimitry Andric   WindowSP m_main_window_sp;
3621349cc55cSDimitry Andric 
3622349cc55cSDimitry Andric   ArgumentsFieldDelegate *m_arguments_field;
3623349cc55cSDimitry Andric   EnvironmentVariableListFieldDelegate *m_target_environment_field;
3624349cc55cSDimitry Andric   DirectoryFieldDelegate *m_working_directory_field;
3625349cc55cSDimitry Andric 
3626349cc55cSDimitry Andric   BooleanFieldDelegate *m_show_advanced_field;
3627349cc55cSDimitry Andric 
3628349cc55cSDimitry Andric   BooleanFieldDelegate *m_stop_at_entry_field;
3629349cc55cSDimitry Andric   BooleanFieldDelegate *m_detach_on_error_field;
3630349cc55cSDimitry Andric   BooleanFieldDelegate *m_disable_aslr_field;
3631349cc55cSDimitry Andric   ProcessPluginFieldDelegate *m_plugin_field;
3632349cc55cSDimitry Andric   ArchFieldDelegate *m_arch_field;
3633349cc55cSDimitry Andric   FileFieldDelegate *m_shell_field;
3634349cc55cSDimitry Andric   BooleanFieldDelegate *m_expand_shell_arguments_field;
3635349cc55cSDimitry Andric   BooleanFieldDelegate *m_disable_standard_io_field;
3636349cc55cSDimitry Andric   FileFieldDelegate *m_standard_input_field;
3637349cc55cSDimitry Andric   FileFieldDelegate *m_standard_output_field;
3638349cc55cSDimitry Andric   FileFieldDelegate *m_standard_error_field;
3639349cc55cSDimitry Andric 
3640349cc55cSDimitry Andric   BooleanFieldDelegate *m_show_inherited_environment_field;
3641349cc55cSDimitry Andric   EnvironmentVariableListFieldDelegate *m_inherited_environment_field;
3642349cc55cSDimitry Andric };
3643349cc55cSDimitry Andric 
3644349cc55cSDimitry Andric ////////////
3645349cc55cSDimitry Andric // Searchers
3646349cc55cSDimitry Andric ////////////
3647349cc55cSDimitry Andric 
3648349cc55cSDimitry Andric class SearcherDelegate {
3649349cc55cSDimitry Andric public:
365081ad6265SDimitry Andric   SearcherDelegate() = default;
3651349cc55cSDimitry Andric 
3652349cc55cSDimitry Andric   virtual ~SearcherDelegate() = default;
3653349cc55cSDimitry Andric 
3654349cc55cSDimitry Andric   virtual int GetNumberOfMatches() = 0;
3655349cc55cSDimitry Andric 
3656349cc55cSDimitry Andric   // Get the string that will be displayed for the match at the input index.
3657349cc55cSDimitry Andric   virtual const std::string &GetMatchTextAtIndex(int index) = 0;
3658349cc55cSDimitry Andric 
3659349cc55cSDimitry Andric   // Update the matches of the search. This is executed every time the text
3660349cc55cSDimitry Andric   // field handles an event.
3661349cc55cSDimitry Andric   virtual void UpdateMatches(const std::string &text) = 0;
3662349cc55cSDimitry Andric 
3663349cc55cSDimitry Andric   // Execute the user callback given the index of some match. This is executed
3664349cc55cSDimitry Andric   // once the user selects a match.
3665349cc55cSDimitry Andric   virtual void ExecuteCallback(int match_index) = 0;
3666349cc55cSDimitry Andric };
3667349cc55cSDimitry Andric 
3668349cc55cSDimitry Andric typedef std::shared_ptr<SearcherDelegate> SearcherDelegateSP;
3669349cc55cSDimitry Andric 
3670349cc55cSDimitry Andric class SearcherWindowDelegate : public WindowDelegate {
3671349cc55cSDimitry Andric public:
3672349cc55cSDimitry Andric   SearcherWindowDelegate(SearcherDelegateSP &delegate_sp)
367381ad6265SDimitry Andric       : m_delegate_sp(delegate_sp), m_text_field("Search", "", false) {
3674349cc55cSDimitry Andric     ;
3675349cc55cSDimitry Andric   }
3676349cc55cSDimitry Andric 
3677349cc55cSDimitry Andric   // A completion window is padded by one character from all sides. A text field
3678349cc55cSDimitry Andric   // is first drawn for inputting the searcher request, then a list of matches
3679349cc55cSDimitry Andric   // are displayed in a scrollable list.
3680349cc55cSDimitry Andric   //
3681349cc55cSDimitry Andric   // ___<Searcher Window Name>____________________________
3682349cc55cSDimitry Andric   // |                                                   |
3683349cc55cSDimitry Andric   // | __[Search]_______________________________________ |
3684349cc55cSDimitry Andric   // | |                                               | |
3685349cc55cSDimitry Andric   // | |_______________________________________________| |
3686349cc55cSDimitry Andric   // | - Match 1.                                        |
3687349cc55cSDimitry Andric   // | - Match 2.                                        |
3688349cc55cSDimitry Andric   // | - ...                                             |
3689349cc55cSDimitry Andric   // |                                                   |
3690349cc55cSDimitry Andric   // |____________________________[Press Esc to Cancel]__|
3691349cc55cSDimitry Andric   //
3692349cc55cSDimitry Andric 
3693349cc55cSDimitry Andric   // Get the index of the last visible match. Assuming at least one match
3694349cc55cSDimitry Andric   // exists.
3695349cc55cSDimitry Andric   int GetLastVisibleMatch(int height) {
3696349cc55cSDimitry Andric     int index = m_first_visible_match + height;
3697349cc55cSDimitry Andric     return std::min(index, m_delegate_sp->GetNumberOfMatches()) - 1;
3698349cc55cSDimitry Andric   }
3699349cc55cSDimitry Andric 
3700349cc55cSDimitry Andric   int GetNumberOfVisibleMatches(int height) {
3701349cc55cSDimitry Andric     return GetLastVisibleMatch(height) - m_first_visible_match + 1;
3702349cc55cSDimitry Andric   }
3703349cc55cSDimitry Andric 
3704349cc55cSDimitry Andric   void UpdateScrolling(Surface &surface) {
3705349cc55cSDimitry Andric     if (m_selected_match < m_first_visible_match) {
3706349cc55cSDimitry Andric       m_first_visible_match = m_selected_match;
3707349cc55cSDimitry Andric       return;
3708349cc55cSDimitry Andric     }
3709349cc55cSDimitry Andric 
3710349cc55cSDimitry Andric     int height = surface.GetHeight();
3711349cc55cSDimitry Andric     int last_visible_match = GetLastVisibleMatch(height);
3712349cc55cSDimitry Andric     if (m_selected_match > last_visible_match) {
3713349cc55cSDimitry Andric       m_first_visible_match = m_selected_match - height + 1;
3714349cc55cSDimitry Andric     }
3715349cc55cSDimitry Andric   }
3716349cc55cSDimitry Andric 
3717349cc55cSDimitry Andric   void DrawMatches(Surface &surface) {
3718349cc55cSDimitry Andric     if (m_delegate_sp->GetNumberOfMatches() == 0)
3719349cc55cSDimitry Andric       return;
3720349cc55cSDimitry Andric 
3721349cc55cSDimitry Andric     UpdateScrolling(surface);
3722349cc55cSDimitry Andric 
3723349cc55cSDimitry Andric     int count = GetNumberOfVisibleMatches(surface.GetHeight());
3724349cc55cSDimitry Andric     for (int i = 0; i < count; i++) {
3725349cc55cSDimitry Andric       surface.MoveCursor(1, i);
3726349cc55cSDimitry Andric       int current_match = m_first_visible_match + i;
3727349cc55cSDimitry Andric       if (current_match == m_selected_match)
3728349cc55cSDimitry Andric         surface.AttributeOn(A_REVERSE);
3729349cc55cSDimitry Andric       surface.PutCString(
3730349cc55cSDimitry Andric           m_delegate_sp->GetMatchTextAtIndex(current_match).c_str());
3731349cc55cSDimitry Andric       if (current_match == m_selected_match)
3732349cc55cSDimitry Andric         surface.AttributeOff(A_REVERSE);
3733349cc55cSDimitry Andric     }
3734349cc55cSDimitry Andric   }
3735349cc55cSDimitry Andric 
3736349cc55cSDimitry Andric   void DrawContent(Surface &surface) {
3737349cc55cSDimitry Andric     Rect content_bounds = surface.GetFrame();
3738349cc55cSDimitry Andric     Rect text_field_bounds, matchs_bounds;
3739349cc55cSDimitry Andric     content_bounds.HorizontalSplit(m_text_field.FieldDelegateGetHeight(),
3740349cc55cSDimitry Andric                                    text_field_bounds, matchs_bounds);
3741349cc55cSDimitry Andric     Surface text_field_surface = surface.SubSurface(text_field_bounds);
3742349cc55cSDimitry Andric     Surface matches_surface = surface.SubSurface(matchs_bounds);
3743349cc55cSDimitry Andric 
3744349cc55cSDimitry Andric     m_text_field.FieldDelegateDraw(text_field_surface, true);
3745349cc55cSDimitry Andric     DrawMatches(matches_surface);
3746349cc55cSDimitry Andric   }
3747349cc55cSDimitry Andric 
3748349cc55cSDimitry Andric   bool WindowDelegateDraw(Window &window, bool force) override {
3749349cc55cSDimitry Andric     window.Erase();
3750349cc55cSDimitry Andric 
3751349cc55cSDimitry Andric     window.DrawTitleBox(window.GetName(), "Press Esc to Cancel");
3752349cc55cSDimitry Andric 
3753349cc55cSDimitry Andric     Rect content_bounds = window.GetFrame();
3754349cc55cSDimitry Andric     content_bounds.Inset(2, 2);
3755349cc55cSDimitry Andric     Surface content_surface = window.SubSurface(content_bounds);
3756349cc55cSDimitry Andric 
3757349cc55cSDimitry Andric     DrawContent(content_surface);
3758349cc55cSDimitry Andric     return true;
3759349cc55cSDimitry Andric   }
3760349cc55cSDimitry Andric 
3761349cc55cSDimitry Andric   void SelectNext() {
3762349cc55cSDimitry Andric     if (m_selected_match != m_delegate_sp->GetNumberOfMatches() - 1)
3763349cc55cSDimitry Andric       m_selected_match++;
3764349cc55cSDimitry Andric   }
3765349cc55cSDimitry Andric 
3766349cc55cSDimitry Andric   void SelectPrevious() {
3767349cc55cSDimitry Andric     if (m_selected_match != 0)
3768349cc55cSDimitry Andric       m_selected_match--;
3769349cc55cSDimitry Andric   }
3770349cc55cSDimitry Andric 
3771349cc55cSDimitry Andric   void ExecuteCallback(Window &window) {
3772349cc55cSDimitry Andric     m_delegate_sp->ExecuteCallback(m_selected_match);
3773349cc55cSDimitry Andric     window.GetParent()->RemoveSubWindow(&window);
3774349cc55cSDimitry Andric   }
3775349cc55cSDimitry Andric 
3776349cc55cSDimitry Andric   void UpdateMatches() {
3777349cc55cSDimitry Andric     m_delegate_sp->UpdateMatches(m_text_field.GetText());
3778349cc55cSDimitry Andric     m_selected_match = 0;
3779349cc55cSDimitry Andric   }
3780349cc55cSDimitry Andric 
3781349cc55cSDimitry Andric   HandleCharResult WindowDelegateHandleChar(Window &window, int key) override {
3782349cc55cSDimitry Andric     switch (key) {
3783349cc55cSDimitry Andric     case '\r':
3784349cc55cSDimitry Andric     case '\n':
3785349cc55cSDimitry Andric     case KEY_ENTER:
3786349cc55cSDimitry Andric       ExecuteCallback(window);
3787349cc55cSDimitry Andric       return eKeyHandled;
3788349cc55cSDimitry Andric     case '\t':
3789349cc55cSDimitry Andric     case KEY_DOWN:
3790349cc55cSDimitry Andric       SelectNext();
3791349cc55cSDimitry Andric       return eKeyHandled;
3792349cc55cSDimitry Andric     case KEY_SHIFT_TAB:
3793349cc55cSDimitry Andric     case KEY_UP:
3794349cc55cSDimitry Andric       SelectPrevious();
3795349cc55cSDimitry Andric       return eKeyHandled;
3796349cc55cSDimitry Andric     case KEY_ESCAPE:
3797349cc55cSDimitry Andric       window.GetParent()->RemoveSubWindow(&window);
3798349cc55cSDimitry Andric       return eKeyHandled;
3799349cc55cSDimitry Andric     default:
3800349cc55cSDimitry Andric       break;
3801349cc55cSDimitry Andric     }
3802349cc55cSDimitry Andric 
3803349cc55cSDimitry Andric     if (m_text_field.FieldDelegateHandleChar(key) == eKeyHandled)
3804349cc55cSDimitry Andric       UpdateMatches();
3805349cc55cSDimitry Andric 
3806349cc55cSDimitry Andric     return eKeyHandled;
3807349cc55cSDimitry Andric   }
3808349cc55cSDimitry Andric 
3809349cc55cSDimitry Andric protected:
3810349cc55cSDimitry Andric   SearcherDelegateSP m_delegate_sp;
3811349cc55cSDimitry Andric   TextFieldDelegate m_text_field;
3812349cc55cSDimitry Andric   // The index of the currently selected match.
381381ad6265SDimitry Andric   int m_selected_match = 0;
3814349cc55cSDimitry Andric   // The index of the first visible match.
381581ad6265SDimitry Andric   int m_first_visible_match = 0;
3816349cc55cSDimitry Andric };
3817349cc55cSDimitry Andric 
3818349cc55cSDimitry Andric //////////////////////////////
3819349cc55cSDimitry Andric // Searcher Delegate Instances
3820349cc55cSDimitry Andric //////////////////////////////
3821349cc55cSDimitry Andric 
3822349cc55cSDimitry Andric // This is a searcher delegate wrapper around CommandCompletions common
3823349cc55cSDimitry Andric // callbacks. The callbacks are only given the match string. The completion_mask
3824*06c3fb27SDimitry Andric // can be a combination of lldb::CompletionType.
3825349cc55cSDimitry Andric class CommonCompletionSearcherDelegate : public SearcherDelegate {
3826349cc55cSDimitry Andric public:
3827349cc55cSDimitry Andric   typedef std::function<void(const std::string &)> CallbackType;
3828349cc55cSDimitry Andric 
3829349cc55cSDimitry Andric   CommonCompletionSearcherDelegate(Debugger &debugger, uint32_t completion_mask,
3830349cc55cSDimitry Andric                                    CallbackType callback)
3831349cc55cSDimitry Andric       : m_debugger(debugger), m_completion_mask(completion_mask),
3832349cc55cSDimitry Andric         m_callback(callback) {}
3833349cc55cSDimitry Andric 
3834349cc55cSDimitry Andric   int GetNumberOfMatches() override { return m_matches.GetSize(); }
3835349cc55cSDimitry Andric 
3836349cc55cSDimitry Andric   const std::string &GetMatchTextAtIndex(int index) override {
3837349cc55cSDimitry Andric     return m_matches[index];
3838349cc55cSDimitry Andric   }
3839349cc55cSDimitry Andric 
3840349cc55cSDimitry Andric   void UpdateMatches(const std::string &text) override {
3841349cc55cSDimitry Andric     CompletionResult result;
3842349cc55cSDimitry Andric     CompletionRequest request(text.c_str(), text.size(), result);
3843*06c3fb27SDimitry Andric     lldb_private::CommandCompletions::InvokeCommonCompletionCallbacks(
3844349cc55cSDimitry Andric         m_debugger.GetCommandInterpreter(), m_completion_mask, request,
3845349cc55cSDimitry Andric         nullptr);
3846349cc55cSDimitry Andric     result.GetMatches(m_matches);
3847349cc55cSDimitry Andric   }
3848349cc55cSDimitry Andric 
3849349cc55cSDimitry Andric   void ExecuteCallback(int match_index) override {
3850349cc55cSDimitry Andric     m_callback(m_matches[match_index]);
3851349cc55cSDimitry Andric   }
3852349cc55cSDimitry Andric 
3853349cc55cSDimitry Andric protected:
3854349cc55cSDimitry Andric   Debugger &m_debugger;
3855*06c3fb27SDimitry Andric   // A compound mask from lldb::CompletionType.
3856349cc55cSDimitry Andric   uint32_t m_completion_mask;
3857349cc55cSDimitry Andric   // A callback to execute once the user selects a match. The match is passed to
3858349cc55cSDimitry Andric   // the callback as a string.
3859349cc55cSDimitry Andric   CallbackType m_callback;
3860349cc55cSDimitry Andric   StringList m_matches;
3861349cc55cSDimitry Andric };
3862349cc55cSDimitry Andric 
3863349cc55cSDimitry Andric ////////
3864349cc55cSDimitry Andric // Menus
3865349cc55cSDimitry Andric ////////
3866349cc55cSDimitry Andric 
3867480093f4SDimitry Andric class MenuDelegate {
3868480093f4SDimitry Andric public:
3869480093f4SDimitry Andric   virtual ~MenuDelegate() = default;
3870480093f4SDimitry Andric 
3871480093f4SDimitry Andric   virtual MenuActionResult MenuDelegateAction(Menu &menu) = 0;
3872480093f4SDimitry Andric };
3873480093f4SDimitry Andric 
3874480093f4SDimitry Andric class Menu : public WindowDelegate {
3875480093f4SDimitry Andric public:
3876480093f4SDimitry Andric   enum class Type { Invalid, Bar, Item, Separator };
3877480093f4SDimitry Andric 
3878480093f4SDimitry Andric   // Menubar or separator constructor
3879480093f4SDimitry Andric   Menu(Type type);
3880480093f4SDimitry Andric 
3881480093f4SDimitry Andric   // Menuitem constructor
3882480093f4SDimitry Andric   Menu(const char *name, const char *key_name, int key_value,
3883480093f4SDimitry Andric        uint64_t identifier);
3884480093f4SDimitry Andric 
3885480093f4SDimitry Andric   ~Menu() override = default;
3886480093f4SDimitry Andric 
3887480093f4SDimitry Andric   const MenuDelegateSP &GetDelegate() const { return m_delegate_sp; }
3888480093f4SDimitry Andric 
3889480093f4SDimitry Andric   void SetDelegate(const MenuDelegateSP &delegate_sp) {
3890480093f4SDimitry Andric     m_delegate_sp = delegate_sp;
3891480093f4SDimitry Andric   }
3892480093f4SDimitry Andric 
3893480093f4SDimitry Andric   void RecalculateNameLengths();
3894480093f4SDimitry Andric 
3895480093f4SDimitry Andric   void AddSubmenu(const MenuSP &menu_sp);
3896480093f4SDimitry Andric 
3897480093f4SDimitry Andric   int DrawAndRunMenu(Window &window);
3898480093f4SDimitry Andric 
3899480093f4SDimitry Andric   void DrawMenuTitle(Window &window, bool highlight);
3900480093f4SDimitry Andric 
3901480093f4SDimitry Andric   bool WindowDelegateDraw(Window &window, bool force) override;
3902480093f4SDimitry Andric 
3903480093f4SDimitry Andric   HandleCharResult WindowDelegateHandleChar(Window &window, int key) override;
3904480093f4SDimitry Andric 
3905480093f4SDimitry Andric   MenuActionResult ActionPrivate(Menu &menu) {
3906480093f4SDimitry Andric     MenuActionResult result = MenuActionResult::NotHandled;
3907480093f4SDimitry Andric     if (m_delegate_sp) {
3908480093f4SDimitry Andric       result = m_delegate_sp->MenuDelegateAction(menu);
3909480093f4SDimitry Andric       if (result != MenuActionResult::NotHandled)
3910480093f4SDimitry Andric         return result;
3911480093f4SDimitry Andric     } else if (m_parent) {
3912480093f4SDimitry Andric       result = m_parent->ActionPrivate(menu);
3913480093f4SDimitry Andric       if (result != MenuActionResult::NotHandled)
3914480093f4SDimitry Andric         return result;
3915480093f4SDimitry Andric     }
3916480093f4SDimitry Andric     return m_canned_result;
3917480093f4SDimitry Andric   }
3918480093f4SDimitry Andric 
3919480093f4SDimitry Andric   MenuActionResult Action() {
3920480093f4SDimitry Andric     // Call the recursive action so it can try to handle it with the menu
3921480093f4SDimitry Andric     // delegate, and if not, try our parent menu
3922480093f4SDimitry Andric     return ActionPrivate(*this);
3923480093f4SDimitry Andric   }
3924480093f4SDimitry Andric 
3925480093f4SDimitry Andric   void SetCannedResult(MenuActionResult result) { m_canned_result = result; }
3926480093f4SDimitry Andric 
3927480093f4SDimitry Andric   Menus &GetSubmenus() { return m_submenus; }
3928480093f4SDimitry Andric 
3929480093f4SDimitry Andric   const Menus &GetSubmenus() const { return m_submenus; }
3930480093f4SDimitry Andric 
3931480093f4SDimitry Andric   int GetSelectedSubmenuIndex() const { return m_selected; }
3932480093f4SDimitry Andric 
3933480093f4SDimitry Andric   void SetSelectedSubmenuIndex(int idx) { m_selected = idx; }
3934480093f4SDimitry Andric 
3935480093f4SDimitry Andric   Type GetType() const { return m_type; }
3936480093f4SDimitry Andric 
3937480093f4SDimitry Andric   int GetStartingColumn() const { return m_start_col; }
3938480093f4SDimitry Andric 
3939480093f4SDimitry Andric   void SetStartingColumn(int col) { m_start_col = col; }
3940480093f4SDimitry Andric 
3941480093f4SDimitry Andric   int GetKeyValue() const { return m_key_value; }
3942480093f4SDimitry Andric 
3943480093f4SDimitry Andric   std::string &GetName() { return m_name; }
3944480093f4SDimitry Andric 
3945480093f4SDimitry Andric   int GetDrawWidth() const {
3946480093f4SDimitry Andric     return m_max_submenu_name_length + m_max_submenu_key_name_length + 8;
3947480093f4SDimitry Andric   }
3948480093f4SDimitry Andric 
3949480093f4SDimitry Andric   uint64_t GetIdentifier() const { return m_identifier; }
3950480093f4SDimitry Andric 
3951480093f4SDimitry Andric   void SetIdentifier(uint64_t identifier) { m_identifier = identifier; }
3952480093f4SDimitry Andric 
3953480093f4SDimitry Andric protected:
3954480093f4SDimitry Andric   std::string m_name;
3955480093f4SDimitry Andric   std::string m_key_name;
3956480093f4SDimitry Andric   uint64_t m_identifier;
3957480093f4SDimitry Andric   Type m_type;
3958480093f4SDimitry Andric   int m_key_value;
3959480093f4SDimitry Andric   int m_start_col;
3960480093f4SDimitry Andric   int m_max_submenu_name_length;
3961480093f4SDimitry Andric   int m_max_submenu_key_name_length;
3962480093f4SDimitry Andric   int m_selected;
3963480093f4SDimitry Andric   Menu *m_parent;
3964480093f4SDimitry Andric   Menus m_submenus;
3965480093f4SDimitry Andric   WindowSP m_menu_window_sp;
3966480093f4SDimitry Andric   MenuActionResult m_canned_result;
3967480093f4SDimitry Andric   MenuDelegateSP m_delegate_sp;
3968480093f4SDimitry Andric };
3969480093f4SDimitry Andric 
3970480093f4SDimitry Andric // Menubar or separator constructor
3971480093f4SDimitry Andric Menu::Menu(Type type)
3972480093f4SDimitry Andric     : m_name(), m_key_name(), m_identifier(0), m_type(type), m_key_value(0),
3973480093f4SDimitry Andric       m_start_col(0), m_max_submenu_name_length(0),
3974480093f4SDimitry Andric       m_max_submenu_key_name_length(0), m_selected(0), m_parent(nullptr),
3975480093f4SDimitry Andric       m_submenus(), m_canned_result(MenuActionResult::NotHandled),
3976480093f4SDimitry Andric       m_delegate_sp() {}
3977480093f4SDimitry Andric 
3978480093f4SDimitry Andric // Menuitem constructor
3979480093f4SDimitry Andric Menu::Menu(const char *name, const char *key_name, int key_value,
3980480093f4SDimitry Andric            uint64_t identifier)
3981480093f4SDimitry Andric     : m_name(), m_key_name(), m_identifier(identifier), m_type(Type::Invalid),
3982480093f4SDimitry Andric       m_key_value(key_value), m_start_col(0), m_max_submenu_name_length(0),
3983480093f4SDimitry Andric       m_max_submenu_key_name_length(0), m_selected(0), m_parent(nullptr),
3984480093f4SDimitry Andric       m_submenus(), m_canned_result(MenuActionResult::NotHandled),
3985480093f4SDimitry Andric       m_delegate_sp() {
3986480093f4SDimitry Andric   if (name && name[0]) {
3987480093f4SDimitry Andric     m_name = name;
3988480093f4SDimitry Andric     m_type = Type::Item;
3989480093f4SDimitry Andric     if (key_name && key_name[0])
3990480093f4SDimitry Andric       m_key_name = key_name;
3991480093f4SDimitry Andric   } else {
3992480093f4SDimitry Andric     m_type = Type::Separator;
3993480093f4SDimitry Andric   }
3994480093f4SDimitry Andric }
3995480093f4SDimitry Andric 
3996480093f4SDimitry Andric void Menu::RecalculateNameLengths() {
3997480093f4SDimitry Andric   m_max_submenu_name_length = 0;
3998480093f4SDimitry Andric   m_max_submenu_key_name_length = 0;
3999480093f4SDimitry Andric   Menus &submenus = GetSubmenus();
4000480093f4SDimitry Andric   const size_t num_submenus = submenus.size();
4001480093f4SDimitry Andric   for (size_t i = 0; i < num_submenus; ++i) {
4002480093f4SDimitry Andric     Menu *submenu = submenus[i].get();
4003480093f4SDimitry Andric     if (static_cast<size_t>(m_max_submenu_name_length) < submenu->m_name.size())
4004480093f4SDimitry Andric       m_max_submenu_name_length = submenu->m_name.size();
4005480093f4SDimitry Andric     if (static_cast<size_t>(m_max_submenu_key_name_length) <
4006480093f4SDimitry Andric         submenu->m_key_name.size())
4007480093f4SDimitry Andric       m_max_submenu_key_name_length = submenu->m_key_name.size();
4008480093f4SDimitry Andric   }
4009480093f4SDimitry Andric }
4010480093f4SDimitry Andric 
4011480093f4SDimitry Andric void Menu::AddSubmenu(const MenuSP &menu_sp) {
4012480093f4SDimitry Andric   menu_sp->m_parent = this;
4013480093f4SDimitry Andric   if (static_cast<size_t>(m_max_submenu_name_length) < menu_sp->m_name.size())
4014480093f4SDimitry Andric     m_max_submenu_name_length = menu_sp->m_name.size();
4015480093f4SDimitry Andric   if (static_cast<size_t>(m_max_submenu_key_name_length) <
4016480093f4SDimitry Andric       menu_sp->m_key_name.size())
4017480093f4SDimitry Andric     m_max_submenu_key_name_length = menu_sp->m_key_name.size();
4018480093f4SDimitry Andric   m_submenus.push_back(menu_sp);
4019480093f4SDimitry Andric }
4020480093f4SDimitry Andric 
4021480093f4SDimitry Andric void Menu::DrawMenuTitle(Window &window, bool highlight) {
4022480093f4SDimitry Andric   if (m_type == Type::Separator) {
4023480093f4SDimitry Andric     window.MoveCursor(0, window.GetCursorY());
4024480093f4SDimitry Andric     window.PutChar(ACS_LTEE);
4025480093f4SDimitry Andric     int width = window.GetWidth();
4026480093f4SDimitry Andric     if (width > 2) {
4027480093f4SDimitry Andric       width -= 2;
4028480093f4SDimitry Andric       for (int i = 0; i < width; ++i)
4029480093f4SDimitry Andric         window.PutChar(ACS_HLINE);
4030480093f4SDimitry Andric     }
4031480093f4SDimitry Andric     window.PutChar(ACS_RTEE);
4032480093f4SDimitry Andric   } else {
4033480093f4SDimitry Andric     const int shortcut_key = m_key_value;
4034480093f4SDimitry Andric     bool underlined_shortcut = false;
4035e8d8bef9SDimitry Andric     const attr_t highlight_attr = A_REVERSE;
4036480093f4SDimitry Andric     if (highlight)
4037e8d8bef9SDimitry Andric       window.AttributeOn(highlight_attr);
40385ffd83dbSDimitry Andric     if (llvm::isPrint(shortcut_key)) {
4039480093f4SDimitry Andric       size_t lower_pos = m_name.find(tolower(shortcut_key));
4040480093f4SDimitry Andric       size_t upper_pos = m_name.find(toupper(shortcut_key));
4041480093f4SDimitry Andric       const char *name = m_name.c_str();
4042480093f4SDimitry Andric       size_t pos = std::min<size_t>(lower_pos, upper_pos);
4043480093f4SDimitry Andric       if (pos != std::string::npos) {
4044480093f4SDimitry Andric         underlined_shortcut = true;
4045480093f4SDimitry Andric         if (pos > 0) {
4046480093f4SDimitry Andric           window.PutCString(name, pos);
4047480093f4SDimitry Andric           name += pos;
4048480093f4SDimitry Andric         }
4049480093f4SDimitry Andric         const attr_t shortcut_attr = A_UNDERLINE | A_BOLD;
4050480093f4SDimitry Andric         window.AttributeOn(shortcut_attr);
4051480093f4SDimitry Andric         window.PutChar(name[0]);
4052480093f4SDimitry Andric         window.AttributeOff(shortcut_attr);
4053480093f4SDimitry Andric         name++;
4054480093f4SDimitry Andric         if (name[0])
4055480093f4SDimitry Andric           window.PutCString(name);
4056480093f4SDimitry Andric       }
4057480093f4SDimitry Andric     }
4058480093f4SDimitry Andric 
4059480093f4SDimitry Andric     if (!underlined_shortcut) {
4060480093f4SDimitry Andric       window.PutCString(m_name.c_str());
4061480093f4SDimitry Andric     }
4062480093f4SDimitry Andric 
4063480093f4SDimitry Andric     if (highlight)
4064e8d8bef9SDimitry Andric       window.AttributeOff(highlight_attr);
4065480093f4SDimitry Andric 
4066480093f4SDimitry Andric     if (m_key_name.empty()) {
40675ffd83dbSDimitry Andric       if (!underlined_shortcut && llvm::isPrint(m_key_value)) {
4068e8d8bef9SDimitry Andric         window.AttributeOn(COLOR_PAIR(MagentaOnWhite));
4069480093f4SDimitry Andric         window.Printf(" (%c)", m_key_value);
4070e8d8bef9SDimitry Andric         window.AttributeOff(COLOR_PAIR(MagentaOnWhite));
4071480093f4SDimitry Andric       }
4072480093f4SDimitry Andric     } else {
4073e8d8bef9SDimitry Andric       window.AttributeOn(COLOR_PAIR(MagentaOnWhite));
4074480093f4SDimitry Andric       window.Printf(" (%s)", m_key_name.c_str());
4075e8d8bef9SDimitry Andric       window.AttributeOff(COLOR_PAIR(MagentaOnWhite));
4076480093f4SDimitry Andric     }
4077480093f4SDimitry Andric   }
4078480093f4SDimitry Andric }
4079480093f4SDimitry Andric 
4080480093f4SDimitry Andric bool Menu::WindowDelegateDraw(Window &window, bool force) {
4081480093f4SDimitry Andric   Menus &submenus = GetSubmenus();
4082480093f4SDimitry Andric   const size_t num_submenus = submenus.size();
4083480093f4SDimitry Andric   const int selected_idx = GetSelectedSubmenuIndex();
4084480093f4SDimitry Andric   Menu::Type menu_type = GetType();
4085480093f4SDimitry Andric   switch (menu_type) {
4086480093f4SDimitry Andric   case Menu::Type::Bar: {
4087e8d8bef9SDimitry Andric     window.SetBackground(BlackOnWhite);
4088480093f4SDimitry Andric     window.MoveCursor(0, 0);
4089480093f4SDimitry Andric     for (size_t i = 0; i < num_submenus; ++i) {
4090480093f4SDimitry Andric       Menu *menu = submenus[i].get();
4091480093f4SDimitry Andric       if (i > 0)
4092480093f4SDimitry Andric         window.PutChar(' ');
4093480093f4SDimitry Andric       menu->SetStartingColumn(window.GetCursorX());
4094480093f4SDimitry Andric       window.PutCString("| ");
4095480093f4SDimitry Andric       menu->DrawMenuTitle(window, false);
4096480093f4SDimitry Andric     }
4097480093f4SDimitry Andric     window.PutCString(" |");
4098480093f4SDimitry Andric   } break;
4099480093f4SDimitry Andric 
4100480093f4SDimitry Andric   case Menu::Type::Item: {
4101480093f4SDimitry Andric     int y = 1;
4102480093f4SDimitry Andric     int x = 3;
4103480093f4SDimitry Andric     // Draw the menu
4104480093f4SDimitry Andric     int cursor_x = 0;
4105480093f4SDimitry Andric     int cursor_y = 0;
4106480093f4SDimitry Andric     window.Erase();
4107e8d8bef9SDimitry Andric     window.SetBackground(BlackOnWhite);
4108480093f4SDimitry Andric     window.Box();
4109480093f4SDimitry Andric     for (size_t i = 0; i < num_submenus; ++i) {
4110480093f4SDimitry Andric       const bool is_selected = (i == static_cast<size_t>(selected_idx));
4111480093f4SDimitry Andric       window.MoveCursor(x, y + i);
4112480093f4SDimitry Andric       if (is_selected) {
4113480093f4SDimitry Andric         // Remember where we want the cursor to be
4114480093f4SDimitry Andric         cursor_x = x - 1;
4115480093f4SDimitry Andric         cursor_y = y + i;
4116480093f4SDimitry Andric       }
4117480093f4SDimitry Andric       submenus[i]->DrawMenuTitle(window, is_selected);
4118480093f4SDimitry Andric     }
4119480093f4SDimitry Andric     window.MoveCursor(cursor_x, cursor_y);
4120480093f4SDimitry Andric   } break;
4121480093f4SDimitry Andric 
4122480093f4SDimitry Andric   default:
4123480093f4SDimitry Andric   case Menu::Type::Separator:
4124480093f4SDimitry Andric     break;
4125480093f4SDimitry Andric   }
4126480093f4SDimitry Andric   return true; // Drawing handled...
4127480093f4SDimitry Andric }
4128480093f4SDimitry Andric 
4129480093f4SDimitry Andric HandleCharResult Menu::WindowDelegateHandleChar(Window &window, int key) {
4130480093f4SDimitry Andric   HandleCharResult result = eKeyNotHandled;
4131480093f4SDimitry Andric 
4132480093f4SDimitry Andric   Menus &submenus = GetSubmenus();
4133480093f4SDimitry Andric   const size_t num_submenus = submenus.size();
4134480093f4SDimitry Andric   const int selected_idx = GetSelectedSubmenuIndex();
4135480093f4SDimitry Andric   Menu::Type menu_type = GetType();
4136480093f4SDimitry Andric   if (menu_type == Menu::Type::Bar) {
4137480093f4SDimitry Andric     MenuSP run_menu_sp;
4138480093f4SDimitry Andric     switch (key) {
4139480093f4SDimitry Andric     case KEY_DOWN:
4140480093f4SDimitry Andric     case KEY_UP:
4141480093f4SDimitry Andric       // Show last menu or first menu
4142480093f4SDimitry Andric       if (selected_idx < static_cast<int>(num_submenus))
4143480093f4SDimitry Andric         run_menu_sp = submenus[selected_idx];
4144480093f4SDimitry Andric       else if (!submenus.empty())
4145480093f4SDimitry Andric         run_menu_sp = submenus.front();
4146480093f4SDimitry Andric       result = eKeyHandled;
4147480093f4SDimitry Andric       break;
4148480093f4SDimitry Andric 
4149480093f4SDimitry Andric     case KEY_RIGHT:
4150480093f4SDimitry Andric       ++m_selected;
4151480093f4SDimitry Andric       if (m_selected >= static_cast<int>(num_submenus))
4152480093f4SDimitry Andric         m_selected = 0;
4153480093f4SDimitry Andric       if (m_selected < static_cast<int>(num_submenus))
4154480093f4SDimitry Andric         run_menu_sp = submenus[m_selected];
4155480093f4SDimitry Andric       else if (!submenus.empty())
4156480093f4SDimitry Andric         run_menu_sp = submenus.front();
4157480093f4SDimitry Andric       result = eKeyHandled;
4158480093f4SDimitry Andric       break;
4159480093f4SDimitry Andric 
4160480093f4SDimitry Andric     case KEY_LEFT:
4161480093f4SDimitry Andric       --m_selected;
4162480093f4SDimitry Andric       if (m_selected < 0)
4163480093f4SDimitry Andric         m_selected = num_submenus - 1;
4164480093f4SDimitry Andric       if (m_selected < static_cast<int>(num_submenus))
4165480093f4SDimitry Andric         run_menu_sp = submenus[m_selected];
4166480093f4SDimitry Andric       else if (!submenus.empty())
4167480093f4SDimitry Andric         run_menu_sp = submenus.front();
4168480093f4SDimitry Andric       result = eKeyHandled;
4169480093f4SDimitry Andric       break;
4170480093f4SDimitry Andric 
4171480093f4SDimitry Andric     default:
4172480093f4SDimitry Andric       for (size_t i = 0; i < num_submenus; ++i) {
4173480093f4SDimitry Andric         if (submenus[i]->GetKeyValue() == key) {
4174480093f4SDimitry Andric           SetSelectedSubmenuIndex(i);
4175480093f4SDimitry Andric           run_menu_sp = submenus[i];
4176480093f4SDimitry Andric           result = eKeyHandled;
4177480093f4SDimitry Andric           break;
4178480093f4SDimitry Andric         }
4179480093f4SDimitry Andric       }
4180480093f4SDimitry Andric       break;
4181480093f4SDimitry Andric     }
4182480093f4SDimitry Andric 
4183480093f4SDimitry Andric     if (run_menu_sp) {
4184480093f4SDimitry Andric       // Run the action on this menu in case we need to populate the menu with
4185480093f4SDimitry Andric       // dynamic content and also in case check marks, and any other menu
4186480093f4SDimitry Andric       // decorations need to be calculated
4187480093f4SDimitry Andric       if (run_menu_sp->Action() == MenuActionResult::Quit)
4188480093f4SDimitry Andric         return eQuitApplication;
4189480093f4SDimitry Andric 
4190480093f4SDimitry Andric       Rect menu_bounds;
4191480093f4SDimitry Andric       menu_bounds.origin.x = run_menu_sp->GetStartingColumn();
4192480093f4SDimitry Andric       menu_bounds.origin.y = 1;
4193480093f4SDimitry Andric       menu_bounds.size.width = run_menu_sp->GetDrawWidth();
4194480093f4SDimitry Andric       menu_bounds.size.height = run_menu_sp->GetSubmenus().size() + 2;
4195480093f4SDimitry Andric       if (m_menu_window_sp)
4196480093f4SDimitry Andric         window.GetParent()->RemoveSubWindow(m_menu_window_sp.get());
4197480093f4SDimitry Andric 
4198480093f4SDimitry Andric       m_menu_window_sp = window.GetParent()->CreateSubWindow(
4199480093f4SDimitry Andric           run_menu_sp->GetName().c_str(), menu_bounds, true);
4200480093f4SDimitry Andric       m_menu_window_sp->SetDelegate(run_menu_sp);
4201480093f4SDimitry Andric     }
4202480093f4SDimitry Andric   } else if (menu_type == Menu::Type::Item) {
4203480093f4SDimitry Andric     switch (key) {
4204480093f4SDimitry Andric     case KEY_DOWN:
4205480093f4SDimitry Andric       if (m_submenus.size() > 1) {
4206480093f4SDimitry Andric         const int start_select = m_selected;
4207480093f4SDimitry Andric         while (++m_selected != start_select) {
4208480093f4SDimitry Andric           if (static_cast<size_t>(m_selected) >= num_submenus)
4209480093f4SDimitry Andric             m_selected = 0;
4210480093f4SDimitry Andric           if (m_submenus[m_selected]->GetType() == Type::Separator)
4211480093f4SDimitry Andric             continue;
4212480093f4SDimitry Andric           else
4213480093f4SDimitry Andric             break;
4214480093f4SDimitry Andric         }
4215480093f4SDimitry Andric         return eKeyHandled;
4216480093f4SDimitry Andric       }
4217480093f4SDimitry Andric       break;
4218480093f4SDimitry Andric 
4219480093f4SDimitry Andric     case KEY_UP:
4220480093f4SDimitry Andric       if (m_submenus.size() > 1) {
4221480093f4SDimitry Andric         const int start_select = m_selected;
4222480093f4SDimitry Andric         while (--m_selected != start_select) {
4223480093f4SDimitry Andric           if (m_selected < static_cast<int>(0))
4224480093f4SDimitry Andric             m_selected = num_submenus - 1;
4225480093f4SDimitry Andric           if (m_submenus[m_selected]->GetType() == Type::Separator)
4226480093f4SDimitry Andric             continue;
4227480093f4SDimitry Andric           else
4228480093f4SDimitry Andric             break;
4229480093f4SDimitry Andric         }
4230480093f4SDimitry Andric         return eKeyHandled;
4231480093f4SDimitry Andric       }
4232480093f4SDimitry Andric       break;
4233480093f4SDimitry Andric 
4234480093f4SDimitry Andric     case KEY_RETURN:
4235480093f4SDimitry Andric       if (static_cast<size_t>(selected_idx) < num_submenus) {
4236480093f4SDimitry Andric         if (submenus[selected_idx]->Action() == MenuActionResult::Quit)
4237480093f4SDimitry Andric           return eQuitApplication;
4238480093f4SDimitry Andric         window.GetParent()->RemoveSubWindow(&window);
4239480093f4SDimitry Andric         return eKeyHandled;
4240480093f4SDimitry Andric       }
4241480093f4SDimitry Andric       break;
4242480093f4SDimitry Andric 
4243480093f4SDimitry Andric     case KEY_ESCAPE: // Beware: pressing escape key has 1 to 2 second delay in
4244480093f4SDimitry Andric                      // case other chars are entered for escaped sequences
4245480093f4SDimitry Andric       window.GetParent()->RemoveSubWindow(&window);
4246480093f4SDimitry Andric       return eKeyHandled;
4247480093f4SDimitry Andric 
4248480093f4SDimitry Andric     default:
4249480093f4SDimitry Andric       for (size_t i = 0; i < num_submenus; ++i) {
4250480093f4SDimitry Andric         Menu *menu = submenus[i].get();
4251480093f4SDimitry Andric         if (menu->GetKeyValue() == key) {
4252480093f4SDimitry Andric           SetSelectedSubmenuIndex(i);
4253480093f4SDimitry Andric           window.GetParent()->RemoveSubWindow(&window);
4254480093f4SDimitry Andric           if (menu->Action() == MenuActionResult::Quit)
4255480093f4SDimitry Andric             return eQuitApplication;
4256480093f4SDimitry Andric           return eKeyHandled;
4257480093f4SDimitry Andric         }
4258480093f4SDimitry Andric       }
4259480093f4SDimitry Andric       break;
4260480093f4SDimitry Andric     }
4261480093f4SDimitry Andric   } else if (menu_type == Menu::Type::Separator) {
4262480093f4SDimitry Andric   }
4263480093f4SDimitry Andric   return result;
4264480093f4SDimitry Andric }
4265480093f4SDimitry Andric 
4266480093f4SDimitry Andric class Application {
4267480093f4SDimitry Andric public:
426881ad6265SDimitry Andric   Application(FILE *in, FILE *out) : m_window_sp(), m_in(in), m_out(out) {}
4269480093f4SDimitry Andric 
4270480093f4SDimitry Andric   ~Application() {
4271480093f4SDimitry Andric     m_window_delegates.clear();
4272480093f4SDimitry Andric     m_window_sp.reset();
4273480093f4SDimitry Andric     if (m_screen) {
4274480093f4SDimitry Andric       ::delscreen(m_screen);
4275480093f4SDimitry Andric       m_screen = nullptr;
4276480093f4SDimitry Andric     }
4277480093f4SDimitry Andric   }
4278480093f4SDimitry Andric 
4279480093f4SDimitry Andric   void Initialize() {
4280480093f4SDimitry Andric     m_screen = ::newterm(nullptr, m_out, m_in);
4281480093f4SDimitry Andric     ::start_color();
4282480093f4SDimitry Andric     ::curs_set(0);
4283480093f4SDimitry Andric     ::noecho();
4284480093f4SDimitry Andric     ::keypad(stdscr, TRUE);
4285480093f4SDimitry Andric   }
4286480093f4SDimitry Andric 
4287480093f4SDimitry Andric   void Terminate() { ::endwin(); }
4288480093f4SDimitry Andric 
4289480093f4SDimitry Andric   void Run(Debugger &debugger) {
4290480093f4SDimitry Andric     bool done = false;
4291480093f4SDimitry Andric     int delay_in_tenths_of_a_second = 1;
4292480093f4SDimitry Andric 
4293349cc55cSDimitry Andric     // Alas the threading model in curses is a bit lame so we need to resort
4294349cc55cSDimitry Andric     // to polling every 0.5 seconds. We could poll for stdin ourselves and
4295349cc55cSDimitry Andric     // then pass the keys down but then we need to translate all of the escape
4296480093f4SDimitry Andric     // sequences ourselves. So we resort to polling for input because we need
4297480093f4SDimitry Andric     // to receive async process events while in this loop.
4298480093f4SDimitry Andric 
4299349cc55cSDimitry Andric     halfdelay(delay_in_tenths_of_a_second); // Poll using some number of
4300349cc55cSDimitry Andric                                             // tenths of seconds seconds when
4301349cc55cSDimitry Andric                                             // calling Window::GetChar()
4302480093f4SDimitry Andric 
4303480093f4SDimitry Andric     ListenerSP listener_sp(
4304480093f4SDimitry Andric         Listener::MakeListener("lldb.IOHandler.curses.Application"));
4305480093f4SDimitry Andric     ConstString broadcaster_class_process(Process::GetStaticBroadcasterClass());
4306480093f4SDimitry Andric     debugger.EnableForwardEvents(listener_sp);
4307480093f4SDimitry Andric 
4308e8d8bef9SDimitry Andric     m_update_screen = true;
4309480093f4SDimitry Andric #if defined(__APPLE__)
4310480093f4SDimitry Andric     std::deque<int> escape_chars;
4311480093f4SDimitry Andric #endif
4312480093f4SDimitry Andric 
4313480093f4SDimitry Andric     while (!done) {
4314e8d8bef9SDimitry Andric       if (m_update_screen) {
4315480093f4SDimitry Andric         m_window_sp->Draw(false);
4316480093f4SDimitry Andric         // All windows should be calling Window::DeferredRefresh() instead of
4317480093f4SDimitry Andric         // Window::Refresh() so we can do a single update and avoid any screen
4318480093f4SDimitry Andric         // blinking
4319480093f4SDimitry Andric         update_panels();
4320480093f4SDimitry Andric 
4321480093f4SDimitry Andric         // Cursor hiding isn't working on MacOSX, so hide it in the top left
4322480093f4SDimitry Andric         // corner
4323480093f4SDimitry Andric         m_window_sp->MoveCursor(0, 0);
4324480093f4SDimitry Andric 
4325480093f4SDimitry Andric         doupdate();
4326e8d8bef9SDimitry Andric         m_update_screen = false;
4327480093f4SDimitry Andric       }
4328480093f4SDimitry Andric 
4329480093f4SDimitry Andric #if defined(__APPLE__)
4330480093f4SDimitry Andric       // Terminal.app doesn't map its function keys correctly, F1-F4 default
4331480093f4SDimitry Andric       // to: \033OP, \033OQ, \033OR, \033OS, so lets take care of this here if
4332480093f4SDimitry Andric       // possible
4333480093f4SDimitry Andric       int ch;
4334480093f4SDimitry Andric       if (escape_chars.empty())
4335480093f4SDimitry Andric         ch = m_window_sp->GetChar();
4336480093f4SDimitry Andric       else {
4337480093f4SDimitry Andric         ch = escape_chars.front();
4338480093f4SDimitry Andric         escape_chars.pop_front();
4339480093f4SDimitry Andric       }
4340480093f4SDimitry Andric       if (ch == KEY_ESCAPE) {
4341480093f4SDimitry Andric         int ch2 = m_window_sp->GetChar();
4342480093f4SDimitry Andric         if (ch2 == 'O') {
4343480093f4SDimitry Andric           int ch3 = m_window_sp->GetChar();
4344480093f4SDimitry Andric           switch (ch3) {
4345480093f4SDimitry Andric           case 'P':
4346480093f4SDimitry Andric             ch = KEY_F(1);
4347480093f4SDimitry Andric             break;
4348480093f4SDimitry Andric           case 'Q':
4349480093f4SDimitry Andric             ch = KEY_F(2);
4350480093f4SDimitry Andric             break;
4351480093f4SDimitry Andric           case 'R':
4352480093f4SDimitry Andric             ch = KEY_F(3);
4353480093f4SDimitry Andric             break;
4354480093f4SDimitry Andric           case 'S':
4355480093f4SDimitry Andric             ch = KEY_F(4);
4356480093f4SDimitry Andric             break;
4357480093f4SDimitry Andric           default:
4358480093f4SDimitry Andric             escape_chars.push_back(ch2);
4359480093f4SDimitry Andric             if (ch3 != -1)
4360480093f4SDimitry Andric               escape_chars.push_back(ch3);
4361480093f4SDimitry Andric             break;
4362480093f4SDimitry Andric           }
4363480093f4SDimitry Andric         } else if (ch2 != -1)
4364480093f4SDimitry Andric           escape_chars.push_back(ch2);
4365480093f4SDimitry Andric       }
4366480093f4SDimitry Andric #else
4367480093f4SDimitry Andric       int ch = m_window_sp->GetChar();
4368480093f4SDimitry Andric 
4369480093f4SDimitry Andric #endif
4370480093f4SDimitry Andric       if (ch == -1) {
4371480093f4SDimitry Andric         if (feof(m_in) || ferror(m_in)) {
4372480093f4SDimitry Andric           done = true;
4373480093f4SDimitry Andric         } else {
4374480093f4SDimitry Andric           // Just a timeout from using halfdelay(), check for events
4375480093f4SDimitry Andric           EventSP event_sp;
4376480093f4SDimitry Andric           while (listener_sp->PeekAtNextEvent()) {
4377480093f4SDimitry Andric             listener_sp->GetEvent(event_sp, std::chrono::seconds(0));
4378480093f4SDimitry Andric 
4379480093f4SDimitry Andric             if (event_sp) {
4380480093f4SDimitry Andric               Broadcaster *broadcaster = event_sp->GetBroadcaster();
4381480093f4SDimitry Andric               if (broadcaster) {
4382480093f4SDimitry Andric                 // uint32_t event_type = event_sp->GetType();
4383480093f4SDimitry Andric                 ConstString broadcaster_class(
4384480093f4SDimitry Andric                     broadcaster->GetBroadcasterClass());
4385480093f4SDimitry Andric                 if (broadcaster_class == broadcaster_class_process) {
4386e8d8bef9SDimitry Andric                   m_update_screen = true;
4387480093f4SDimitry Andric                   continue; // Don't get any key, just update our view
4388480093f4SDimitry Andric                 }
4389480093f4SDimitry Andric               }
4390480093f4SDimitry Andric             }
4391480093f4SDimitry Andric           }
4392480093f4SDimitry Andric         }
4393480093f4SDimitry Andric       } else {
4394480093f4SDimitry Andric         HandleCharResult key_result = m_window_sp->HandleChar(ch);
4395480093f4SDimitry Andric         switch (key_result) {
4396480093f4SDimitry Andric         case eKeyHandled:
4397e8d8bef9SDimitry Andric           m_update_screen = true;
4398480093f4SDimitry Andric           break;
4399480093f4SDimitry Andric         case eKeyNotHandled:
4400e8d8bef9SDimitry Andric           if (ch == 12) { // Ctrl+L, force full redraw
4401e8d8bef9SDimitry Andric             redrawwin(m_window_sp->get());
4402e8d8bef9SDimitry Andric             m_update_screen = true;
4403e8d8bef9SDimitry Andric           }
4404480093f4SDimitry Andric           break;
4405480093f4SDimitry Andric         case eQuitApplication:
4406480093f4SDimitry Andric           done = true;
4407480093f4SDimitry Andric           break;
4408480093f4SDimitry Andric         }
4409480093f4SDimitry Andric       }
4410480093f4SDimitry Andric     }
4411480093f4SDimitry Andric 
4412480093f4SDimitry Andric     debugger.CancelForwardEvents(listener_sp);
4413480093f4SDimitry Andric   }
4414480093f4SDimitry Andric 
4415480093f4SDimitry Andric   WindowSP &GetMainWindow() {
4416480093f4SDimitry Andric     if (!m_window_sp)
4417480093f4SDimitry Andric       m_window_sp = std::make_shared<Window>("main", stdscr, false);
4418480093f4SDimitry Andric     return m_window_sp;
4419480093f4SDimitry Andric   }
4420480093f4SDimitry Andric 
4421e8d8bef9SDimitry Andric   void TerminalSizeChanged() {
4422e8d8bef9SDimitry Andric     ::endwin();
4423e8d8bef9SDimitry Andric     ::refresh();
4424e8d8bef9SDimitry Andric     Rect content_bounds = m_window_sp->GetFrame();
4425e8d8bef9SDimitry Andric     m_window_sp->SetBounds(content_bounds);
4426e8d8bef9SDimitry Andric     if (WindowSP menubar_window_sp = m_window_sp->FindSubWindow("Menubar"))
4427e8d8bef9SDimitry Andric       menubar_window_sp->SetBounds(content_bounds.MakeMenuBar());
4428e8d8bef9SDimitry Andric     if (WindowSP status_window_sp = m_window_sp->FindSubWindow("Status"))
4429e8d8bef9SDimitry Andric       status_window_sp->SetBounds(content_bounds.MakeStatusBar());
4430e8d8bef9SDimitry Andric 
4431e8d8bef9SDimitry Andric     WindowSP source_window_sp = m_window_sp->FindSubWindow("Source");
4432e8d8bef9SDimitry Andric     WindowSP variables_window_sp = m_window_sp->FindSubWindow("Variables");
4433e8d8bef9SDimitry Andric     WindowSP registers_window_sp = m_window_sp->FindSubWindow("Registers");
4434e8d8bef9SDimitry Andric     WindowSP threads_window_sp = m_window_sp->FindSubWindow("Threads");
4435e8d8bef9SDimitry Andric 
4436e8d8bef9SDimitry Andric     Rect threads_bounds;
4437e8d8bef9SDimitry Andric     Rect source_variables_bounds;
4438e8d8bef9SDimitry Andric     content_bounds.VerticalSplitPercentage(0.80, source_variables_bounds,
4439e8d8bef9SDimitry Andric                                            threads_bounds);
4440e8d8bef9SDimitry Andric     if (threads_window_sp)
4441e8d8bef9SDimitry Andric       threads_window_sp->SetBounds(threads_bounds);
4442e8d8bef9SDimitry Andric     else
4443e8d8bef9SDimitry Andric       source_variables_bounds = content_bounds;
4444e8d8bef9SDimitry Andric 
4445e8d8bef9SDimitry Andric     Rect source_bounds;
4446e8d8bef9SDimitry Andric     Rect variables_registers_bounds;
4447e8d8bef9SDimitry Andric     source_variables_bounds.HorizontalSplitPercentage(
4448e8d8bef9SDimitry Andric         0.70, source_bounds, variables_registers_bounds);
4449e8d8bef9SDimitry Andric     if (variables_window_sp || registers_window_sp) {
4450e8d8bef9SDimitry Andric       if (variables_window_sp && registers_window_sp) {
4451e8d8bef9SDimitry Andric         Rect variables_bounds;
4452e8d8bef9SDimitry Andric         Rect registers_bounds;
4453e8d8bef9SDimitry Andric         variables_registers_bounds.VerticalSplitPercentage(
4454e8d8bef9SDimitry Andric             0.50, variables_bounds, registers_bounds);
4455e8d8bef9SDimitry Andric         variables_window_sp->SetBounds(variables_bounds);
4456e8d8bef9SDimitry Andric         registers_window_sp->SetBounds(registers_bounds);
4457e8d8bef9SDimitry Andric       } else if (variables_window_sp) {
4458e8d8bef9SDimitry Andric         variables_window_sp->SetBounds(variables_registers_bounds);
4459e8d8bef9SDimitry Andric       } else {
4460e8d8bef9SDimitry Andric         registers_window_sp->SetBounds(variables_registers_bounds);
4461e8d8bef9SDimitry Andric       }
4462e8d8bef9SDimitry Andric     } else {
4463e8d8bef9SDimitry Andric       source_bounds = source_variables_bounds;
4464e8d8bef9SDimitry Andric     }
4465e8d8bef9SDimitry Andric 
4466e8d8bef9SDimitry Andric     source_window_sp->SetBounds(source_bounds);
4467e8d8bef9SDimitry Andric 
4468e8d8bef9SDimitry Andric     touchwin(stdscr);
4469e8d8bef9SDimitry Andric     redrawwin(m_window_sp->get());
4470e8d8bef9SDimitry Andric     m_update_screen = true;
4471e8d8bef9SDimitry Andric   }
4472e8d8bef9SDimitry Andric 
4473480093f4SDimitry Andric protected:
4474480093f4SDimitry Andric   WindowSP m_window_sp;
4475480093f4SDimitry Andric   WindowDelegates m_window_delegates;
447681ad6265SDimitry Andric   SCREEN *m_screen = nullptr;
4477480093f4SDimitry Andric   FILE *m_in;
4478480093f4SDimitry Andric   FILE *m_out;
4479e8d8bef9SDimitry Andric   bool m_update_screen = false;
4480480093f4SDimitry Andric };
4481480093f4SDimitry Andric 
4482480093f4SDimitry Andric } // namespace curses
4483480093f4SDimitry Andric 
4484480093f4SDimitry Andric using namespace curses;
4485480093f4SDimitry Andric 
4486480093f4SDimitry Andric struct Row {
4487fe6060f1SDimitry Andric   ValueObjectUpdater value;
4488480093f4SDimitry Andric   Row *parent;
4489480093f4SDimitry Andric   // The process stop ID when the children were calculated.
4490e8d8bef9SDimitry Andric   uint32_t children_stop_id = 0;
4491e8d8bef9SDimitry Andric   int row_idx = 0;
4492e8d8bef9SDimitry Andric   int x = 1;
4493e8d8bef9SDimitry Andric   int y = 1;
4494480093f4SDimitry Andric   bool might_have_children;
4495e8d8bef9SDimitry Andric   bool expanded = false;
4496e8d8bef9SDimitry Andric   bool calculated_children = false;
4497480093f4SDimitry Andric   std::vector<Row> children;
4498480093f4SDimitry Andric 
4499480093f4SDimitry Andric   Row(const ValueObjectSP &v, Row *p)
4500fe6060f1SDimitry Andric       : value(v), parent(p),
4501e8d8bef9SDimitry Andric         might_have_children(v ? v->MightHaveChildren() : false) {}
4502480093f4SDimitry Andric 
4503480093f4SDimitry Andric   size_t GetDepth() const {
4504480093f4SDimitry Andric     if (parent)
4505480093f4SDimitry Andric       return 1 + parent->GetDepth();
4506480093f4SDimitry Andric     return 0;
4507480093f4SDimitry Andric   }
4508480093f4SDimitry Andric 
4509480093f4SDimitry Andric   void Expand() { expanded = true; }
4510480093f4SDimitry Andric 
4511480093f4SDimitry Andric   std::vector<Row> &GetChildren() {
4512480093f4SDimitry Andric     ProcessSP process_sp = value.GetProcessSP();
4513480093f4SDimitry Andric     auto stop_id = process_sp->GetStopID();
4514480093f4SDimitry Andric     if (process_sp && stop_id != children_stop_id) {
4515480093f4SDimitry Andric       children_stop_id = stop_id;
4516480093f4SDimitry Andric       calculated_children = false;
4517480093f4SDimitry Andric     }
4518480093f4SDimitry Andric     if (!calculated_children) {
4519480093f4SDimitry Andric       children.clear();
4520480093f4SDimitry Andric       calculated_children = true;
4521480093f4SDimitry Andric       ValueObjectSP valobj = value.GetSP();
4522480093f4SDimitry Andric       if (valobj) {
4523480093f4SDimitry Andric         const size_t num_children = valobj->GetNumChildren();
4524480093f4SDimitry Andric         for (size_t i = 0; i < num_children; ++i) {
4525*06c3fb27SDimitry Andric           children.push_back(Row(valobj->GetChildAtIndex(i), this));
4526480093f4SDimitry Andric         }
4527480093f4SDimitry Andric       }
4528480093f4SDimitry Andric     }
4529480093f4SDimitry Andric     return children;
4530480093f4SDimitry Andric   }
4531480093f4SDimitry Andric 
4532480093f4SDimitry Andric   void Unexpand() {
4533480093f4SDimitry Andric     expanded = false;
4534480093f4SDimitry Andric     calculated_children = false;
4535480093f4SDimitry Andric     children.clear();
4536480093f4SDimitry Andric   }
4537480093f4SDimitry Andric 
4538480093f4SDimitry Andric   void DrawTree(Window &window) {
4539480093f4SDimitry Andric     if (parent)
4540480093f4SDimitry Andric       parent->DrawTreeForChild(window, this, 0);
4541480093f4SDimitry Andric 
454281ad6265SDimitry Andric     if (might_have_children &&
454381ad6265SDimitry Andric         (!calculated_children || !GetChildren().empty())) {
4544480093f4SDimitry Andric       // It we can get UTF8 characters to work we should try to use the
4545480093f4SDimitry Andric       // "symbol" UTF8 string below
4546480093f4SDimitry Andric       //            const char *symbol = "";
4547480093f4SDimitry Andric       //            if (row.expanded)
4548480093f4SDimitry Andric       //                symbol = "\xe2\x96\xbd ";
4549480093f4SDimitry Andric       //            else
4550480093f4SDimitry Andric       //                symbol = "\xe2\x96\xb7 ";
4551480093f4SDimitry Andric       //            window.PutCString (symbol);
4552480093f4SDimitry Andric 
4553480093f4SDimitry Andric       // The ACS_DARROW and ACS_RARROW don't look very nice they are just a 'v'
4554480093f4SDimitry Andric       // or '>' character...
4555480093f4SDimitry Andric       //            if (expanded)
4556480093f4SDimitry Andric       //                window.PutChar (ACS_DARROW);
4557480093f4SDimitry Andric       //            else
4558480093f4SDimitry Andric       //                window.PutChar (ACS_RARROW);
4559480093f4SDimitry Andric       // Since we can't find any good looking right arrow/down arrow symbols,
4560480093f4SDimitry Andric       // just use a diamond...
4561480093f4SDimitry Andric       window.PutChar(ACS_DIAMOND);
4562480093f4SDimitry Andric       window.PutChar(ACS_HLINE);
4563480093f4SDimitry Andric     }
4564480093f4SDimitry Andric   }
4565480093f4SDimitry Andric 
4566480093f4SDimitry Andric   void DrawTreeForChild(Window &window, Row *child, uint32_t reverse_depth) {
4567480093f4SDimitry Andric     if (parent)
4568480093f4SDimitry Andric       parent->DrawTreeForChild(window, this, reverse_depth + 1);
4569480093f4SDimitry Andric 
4570480093f4SDimitry Andric     if (&GetChildren().back() == child) {
4571480093f4SDimitry Andric       // Last child
4572480093f4SDimitry Andric       if (reverse_depth == 0) {
4573480093f4SDimitry Andric         window.PutChar(ACS_LLCORNER);
4574480093f4SDimitry Andric         window.PutChar(ACS_HLINE);
4575480093f4SDimitry Andric       } else {
4576480093f4SDimitry Andric         window.PutChar(' ');
4577480093f4SDimitry Andric         window.PutChar(' ');
4578480093f4SDimitry Andric       }
4579480093f4SDimitry Andric     } else {
4580480093f4SDimitry Andric       if (reverse_depth == 0) {
4581480093f4SDimitry Andric         window.PutChar(ACS_LTEE);
4582480093f4SDimitry Andric         window.PutChar(ACS_HLINE);
4583480093f4SDimitry Andric       } else {
4584480093f4SDimitry Andric         window.PutChar(ACS_VLINE);
4585480093f4SDimitry Andric         window.PutChar(' ');
4586480093f4SDimitry Andric       }
4587480093f4SDimitry Andric     }
4588480093f4SDimitry Andric   }
4589480093f4SDimitry Andric };
4590480093f4SDimitry Andric 
4591480093f4SDimitry Andric struct DisplayOptions {
4592480093f4SDimitry Andric   bool show_types;
4593480093f4SDimitry Andric };
4594480093f4SDimitry Andric 
4595480093f4SDimitry Andric class TreeItem;
4596480093f4SDimitry Andric 
4597480093f4SDimitry Andric class TreeDelegate {
4598480093f4SDimitry Andric public:
4599480093f4SDimitry Andric   TreeDelegate() = default;
4600480093f4SDimitry Andric   virtual ~TreeDelegate() = default;
4601480093f4SDimitry Andric 
4602480093f4SDimitry Andric   virtual void TreeDelegateDrawTreeItem(TreeItem &item, Window &window) = 0;
4603480093f4SDimitry Andric   virtual void TreeDelegateGenerateChildren(TreeItem &item) = 0;
4604fe6060f1SDimitry Andric   virtual void TreeDelegateUpdateSelection(TreeItem &root, int &selection_index,
46050eae32dcSDimitry Andric                                            TreeItem *&selected_item) {}
4606349cc55cSDimitry Andric   // This is invoked when a tree item is selected. If true is returned, the
4607349cc55cSDimitry Andric   // views are updated.
4608349cc55cSDimitry Andric   virtual bool TreeDelegateItemSelected(TreeItem &item) = 0;
4609fe6060f1SDimitry Andric   virtual bool TreeDelegateExpandRootByDefault() { return false; }
4610349cc55cSDimitry Andric   // This is mostly useful for root tree delegates. If false is returned,
4611349cc55cSDimitry Andric   // drawing will be skipped completely. This is needed, for instance, in
4612349cc55cSDimitry Andric   // skipping drawing of the threads tree if there is no running process.
4613349cc55cSDimitry Andric   virtual bool TreeDelegateShouldDraw() { return true; }
4614480093f4SDimitry Andric };
4615480093f4SDimitry Andric 
4616480093f4SDimitry Andric typedef std::shared_ptr<TreeDelegate> TreeDelegateSP;
4617480093f4SDimitry Andric 
4618480093f4SDimitry Andric class TreeItem {
4619480093f4SDimitry Andric public:
4620480093f4SDimitry Andric   TreeItem(TreeItem *parent, TreeDelegate &delegate, bool might_have_children)
462181ad6265SDimitry Andric       : m_parent(parent), m_delegate(delegate), m_children(),
462281ad6265SDimitry Andric         m_might_have_children(might_have_children) {
4623fe6060f1SDimitry Andric     if (m_parent == nullptr)
4624fe6060f1SDimitry Andric       m_is_expanded = m_delegate.TreeDelegateExpandRootByDefault();
4625fe6060f1SDimitry Andric   }
4626480093f4SDimitry Andric 
4627480093f4SDimitry Andric   TreeItem &operator=(const TreeItem &rhs) {
4628480093f4SDimitry Andric     if (this != &rhs) {
4629480093f4SDimitry Andric       m_parent = rhs.m_parent;
4630480093f4SDimitry Andric       m_delegate = rhs.m_delegate;
4631480093f4SDimitry Andric       m_user_data = rhs.m_user_data;
4632480093f4SDimitry Andric       m_identifier = rhs.m_identifier;
4633480093f4SDimitry Andric       m_row_idx = rhs.m_row_idx;
4634480093f4SDimitry Andric       m_children = rhs.m_children;
4635480093f4SDimitry Andric       m_might_have_children = rhs.m_might_have_children;
4636480093f4SDimitry Andric       m_is_expanded = rhs.m_is_expanded;
4637480093f4SDimitry Andric     }
4638480093f4SDimitry Andric     return *this;
4639480093f4SDimitry Andric   }
4640480093f4SDimitry Andric 
4641480093f4SDimitry Andric   TreeItem(const TreeItem &) = default;
4642480093f4SDimitry Andric 
4643480093f4SDimitry Andric   size_t GetDepth() const {
4644480093f4SDimitry Andric     if (m_parent)
4645480093f4SDimitry Andric       return 1 + m_parent->GetDepth();
4646480093f4SDimitry Andric     return 0;
4647480093f4SDimitry Andric   }
4648480093f4SDimitry Andric 
4649480093f4SDimitry Andric   int GetRowIndex() const { return m_row_idx; }
4650480093f4SDimitry Andric 
4651480093f4SDimitry Andric   void ClearChildren() { m_children.clear(); }
4652480093f4SDimitry Andric 
4653480093f4SDimitry Andric   void Resize(size_t n, const TreeItem &t) { m_children.resize(n, t); }
4654480093f4SDimitry Andric 
4655480093f4SDimitry Andric   TreeItem &operator[](size_t i) { return m_children[i]; }
4656480093f4SDimitry Andric 
4657480093f4SDimitry Andric   void SetRowIndex(int row_idx) { m_row_idx = row_idx; }
4658480093f4SDimitry Andric 
4659480093f4SDimitry Andric   size_t GetNumChildren() {
4660480093f4SDimitry Andric     m_delegate.TreeDelegateGenerateChildren(*this);
4661480093f4SDimitry Andric     return m_children.size();
4662480093f4SDimitry Andric   }
4663480093f4SDimitry Andric 
4664480093f4SDimitry Andric   void ItemWasSelected() { m_delegate.TreeDelegateItemSelected(*this); }
4665480093f4SDimitry Andric 
4666480093f4SDimitry Andric   void CalculateRowIndexes(int &row_idx) {
4667480093f4SDimitry Andric     SetRowIndex(row_idx);
4668480093f4SDimitry Andric     ++row_idx;
4669480093f4SDimitry Andric 
4670480093f4SDimitry Andric     const bool expanded = IsExpanded();
4671480093f4SDimitry Andric 
4672480093f4SDimitry Andric     // The root item must calculate its children, or we must calculate the
4673480093f4SDimitry Andric     // number of children if the item is expanded
4674480093f4SDimitry Andric     if (m_parent == nullptr || expanded)
4675480093f4SDimitry Andric       GetNumChildren();
4676480093f4SDimitry Andric 
4677480093f4SDimitry Andric     for (auto &item : m_children) {
4678480093f4SDimitry Andric       if (expanded)
4679480093f4SDimitry Andric         item.CalculateRowIndexes(row_idx);
4680480093f4SDimitry Andric       else
4681480093f4SDimitry Andric         item.SetRowIndex(-1);
4682480093f4SDimitry Andric     }
4683480093f4SDimitry Andric   }
4684480093f4SDimitry Andric 
4685480093f4SDimitry Andric   TreeItem *GetParent() { return m_parent; }
4686480093f4SDimitry Andric 
4687480093f4SDimitry Andric   bool IsExpanded() const { return m_is_expanded; }
4688480093f4SDimitry Andric 
4689480093f4SDimitry Andric   void Expand() { m_is_expanded = true; }
4690480093f4SDimitry Andric 
4691480093f4SDimitry Andric   void Unexpand() { m_is_expanded = false; }
4692480093f4SDimitry Andric 
4693480093f4SDimitry Andric   bool Draw(Window &window, const int first_visible_row,
4694480093f4SDimitry Andric             const uint32_t selected_row_idx, int &row_idx, int &num_rows_left) {
4695480093f4SDimitry Andric     if (num_rows_left <= 0)
4696480093f4SDimitry Andric       return false;
4697480093f4SDimitry Andric 
4698480093f4SDimitry Andric     if (m_row_idx >= first_visible_row) {
4699480093f4SDimitry Andric       window.MoveCursor(2, row_idx + 1);
4700480093f4SDimitry Andric 
4701480093f4SDimitry Andric       if (m_parent)
4702480093f4SDimitry Andric         m_parent->DrawTreeForChild(window, this, 0);
4703480093f4SDimitry Andric 
4704480093f4SDimitry Andric       if (m_might_have_children) {
4705480093f4SDimitry Andric         // It we can get UTF8 characters to work we should try to use the
4706480093f4SDimitry Andric         // "symbol" UTF8 string below
4707480093f4SDimitry Andric         //            const char *symbol = "";
4708480093f4SDimitry Andric         //            if (row.expanded)
4709480093f4SDimitry Andric         //                symbol = "\xe2\x96\xbd ";
4710480093f4SDimitry Andric         //            else
4711480093f4SDimitry Andric         //                symbol = "\xe2\x96\xb7 ";
4712480093f4SDimitry Andric         //            window.PutCString (symbol);
4713480093f4SDimitry Andric 
4714480093f4SDimitry Andric         // The ACS_DARROW and ACS_RARROW don't look very nice they are just a
4715480093f4SDimitry Andric         // 'v' or '>' character...
4716480093f4SDimitry Andric         //            if (expanded)
4717480093f4SDimitry Andric         //                window.PutChar (ACS_DARROW);
4718480093f4SDimitry Andric         //            else
4719480093f4SDimitry Andric         //                window.PutChar (ACS_RARROW);
4720480093f4SDimitry Andric         // Since we can't find any good looking right arrow/down arrow symbols,
4721480093f4SDimitry Andric         // just use a diamond...
4722480093f4SDimitry Andric         window.PutChar(ACS_DIAMOND);
4723480093f4SDimitry Andric         window.PutChar(ACS_HLINE);
4724480093f4SDimitry Andric       }
4725480093f4SDimitry Andric       bool highlight = (selected_row_idx == static_cast<size_t>(m_row_idx)) &&
4726480093f4SDimitry Andric                        window.IsActive();
4727480093f4SDimitry Andric 
4728480093f4SDimitry Andric       if (highlight)
4729480093f4SDimitry Andric         window.AttributeOn(A_REVERSE);
4730480093f4SDimitry Andric 
4731480093f4SDimitry Andric       m_delegate.TreeDelegateDrawTreeItem(*this, window);
4732480093f4SDimitry Andric 
4733480093f4SDimitry Andric       if (highlight)
4734480093f4SDimitry Andric         window.AttributeOff(A_REVERSE);
4735480093f4SDimitry Andric       ++row_idx;
4736480093f4SDimitry Andric       --num_rows_left;
4737480093f4SDimitry Andric     }
4738480093f4SDimitry Andric 
4739480093f4SDimitry Andric     if (num_rows_left <= 0)
4740480093f4SDimitry Andric       return false; // We are done drawing...
4741480093f4SDimitry Andric 
4742480093f4SDimitry Andric     if (IsExpanded()) {
4743480093f4SDimitry Andric       for (auto &item : m_children) {
4744480093f4SDimitry Andric         // If we displayed all the rows and item.Draw() returns false we are
4745480093f4SDimitry Andric         // done drawing and can exit this for loop
4746480093f4SDimitry Andric         if (!item.Draw(window, first_visible_row, selected_row_idx, row_idx,
4747480093f4SDimitry Andric                        num_rows_left))
4748480093f4SDimitry Andric           break;
4749480093f4SDimitry Andric       }
4750480093f4SDimitry Andric     }
4751480093f4SDimitry Andric     return num_rows_left >= 0; // Return true if not done drawing yet
4752480093f4SDimitry Andric   }
4753480093f4SDimitry Andric 
4754480093f4SDimitry Andric   void DrawTreeForChild(Window &window, TreeItem *child,
4755480093f4SDimitry Andric                         uint32_t reverse_depth) {
4756480093f4SDimitry Andric     if (m_parent)
4757480093f4SDimitry Andric       m_parent->DrawTreeForChild(window, this, reverse_depth + 1);
4758480093f4SDimitry Andric 
4759480093f4SDimitry Andric     if (&m_children.back() == child) {
4760480093f4SDimitry Andric       // Last child
4761480093f4SDimitry Andric       if (reverse_depth == 0) {
4762480093f4SDimitry Andric         window.PutChar(ACS_LLCORNER);
4763480093f4SDimitry Andric         window.PutChar(ACS_HLINE);
4764480093f4SDimitry Andric       } else {
4765480093f4SDimitry Andric         window.PutChar(' ');
4766480093f4SDimitry Andric         window.PutChar(' ');
4767480093f4SDimitry Andric       }
4768480093f4SDimitry Andric     } else {
4769480093f4SDimitry Andric       if (reverse_depth == 0) {
4770480093f4SDimitry Andric         window.PutChar(ACS_LTEE);
4771480093f4SDimitry Andric         window.PutChar(ACS_HLINE);
4772480093f4SDimitry Andric       } else {
4773480093f4SDimitry Andric         window.PutChar(ACS_VLINE);
4774480093f4SDimitry Andric         window.PutChar(' ');
4775480093f4SDimitry Andric       }
4776480093f4SDimitry Andric     }
4777480093f4SDimitry Andric   }
4778480093f4SDimitry Andric 
4779480093f4SDimitry Andric   TreeItem *GetItemForRowIndex(uint32_t row_idx) {
4780480093f4SDimitry Andric     if (static_cast<uint32_t>(m_row_idx) == row_idx)
4781480093f4SDimitry Andric       return this;
4782480093f4SDimitry Andric     if (m_children.empty())
4783480093f4SDimitry Andric       return nullptr;
4784480093f4SDimitry Andric     if (IsExpanded()) {
4785480093f4SDimitry Andric       for (auto &item : m_children) {
4786480093f4SDimitry Andric         TreeItem *selected_item_ptr = item.GetItemForRowIndex(row_idx);
4787480093f4SDimitry Andric         if (selected_item_ptr)
4788480093f4SDimitry Andric           return selected_item_ptr;
4789480093f4SDimitry Andric       }
4790480093f4SDimitry Andric     }
4791480093f4SDimitry Andric     return nullptr;
4792480093f4SDimitry Andric   }
4793480093f4SDimitry Andric 
4794480093f4SDimitry Andric   void *GetUserData() const { return m_user_data; }
4795480093f4SDimitry Andric 
4796480093f4SDimitry Andric   void SetUserData(void *user_data) { m_user_data = user_data; }
4797480093f4SDimitry Andric 
4798480093f4SDimitry Andric   uint64_t GetIdentifier() const { return m_identifier; }
4799480093f4SDimitry Andric 
4800480093f4SDimitry Andric   void SetIdentifier(uint64_t identifier) { m_identifier = identifier; }
4801480093f4SDimitry Andric 
4802349cc55cSDimitry Andric   const std::string &GetText() const { return m_text; }
4803349cc55cSDimitry Andric 
4804349cc55cSDimitry Andric   void SetText(const char *text) {
4805349cc55cSDimitry Andric     if (text == nullptr) {
4806349cc55cSDimitry Andric       m_text.clear();
4807349cc55cSDimitry Andric       return;
4808349cc55cSDimitry Andric     }
4809349cc55cSDimitry Andric     m_text = text;
4810349cc55cSDimitry Andric   }
4811349cc55cSDimitry Andric 
4812480093f4SDimitry Andric   void SetMightHaveChildren(bool b) { m_might_have_children = b; }
4813480093f4SDimitry Andric 
4814480093f4SDimitry Andric protected:
4815480093f4SDimitry Andric   TreeItem *m_parent;
4816480093f4SDimitry Andric   TreeDelegate &m_delegate;
481781ad6265SDimitry Andric   void *m_user_data = nullptr;
481881ad6265SDimitry Andric   uint64_t m_identifier = 0;
4819349cc55cSDimitry Andric   std::string m_text;
482081ad6265SDimitry Andric   int m_row_idx = -1; // Zero based visible row index, -1 if not visible or for
482181ad6265SDimitry Andric                       // the root item
4822480093f4SDimitry Andric   std::vector<TreeItem> m_children;
4823480093f4SDimitry Andric   bool m_might_have_children;
482481ad6265SDimitry Andric   bool m_is_expanded = false;
4825480093f4SDimitry Andric };
4826480093f4SDimitry Andric 
4827480093f4SDimitry Andric class TreeWindowDelegate : public WindowDelegate {
4828480093f4SDimitry Andric public:
4829480093f4SDimitry Andric   TreeWindowDelegate(Debugger &debugger, const TreeDelegateSP &delegate_sp)
4830480093f4SDimitry Andric       : m_debugger(debugger), m_delegate_sp(delegate_sp),
483181ad6265SDimitry Andric         m_root(nullptr, *delegate_sp, true) {}
4832480093f4SDimitry Andric 
4833480093f4SDimitry Andric   int NumVisibleRows() const { return m_max_y - m_min_y; }
4834480093f4SDimitry Andric 
4835480093f4SDimitry Andric   bool WindowDelegateDraw(Window &window, bool force) override {
4836480093f4SDimitry Andric     m_min_x = 2;
4837480093f4SDimitry Andric     m_min_y = 1;
4838480093f4SDimitry Andric     m_max_x = window.GetWidth() - 1;
4839480093f4SDimitry Andric     m_max_y = window.GetHeight() - 1;
4840480093f4SDimitry Andric 
4841480093f4SDimitry Andric     window.Erase();
4842480093f4SDimitry Andric     window.DrawTitleBox(window.GetName());
4843480093f4SDimitry Andric 
4844349cc55cSDimitry Andric     if (!m_delegate_sp->TreeDelegateShouldDraw()) {
4845349cc55cSDimitry Andric       m_selected_item = nullptr;
4846349cc55cSDimitry Andric       return true;
4847349cc55cSDimitry Andric     }
4848349cc55cSDimitry Andric 
4849480093f4SDimitry Andric     const int num_visible_rows = NumVisibleRows();
4850480093f4SDimitry Andric     m_num_rows = 0;
4851480093f4SDimitry Andric     m_root.CalculateRowIndexes(m_num_rows);
4852fe6060f1SDimitry Andric     m_delegate_sp->TreeDelegateUpdateSelection(m_root, m_selected_row_idx,
4853fe6060f1SDimitry Andric                                                m_selected_item);
4854480093f4SDimitry Andric 
4855480093f4SDimitry Andric     // If we unexpanded while having something selected our total number of
4856480093f4SDimitry Andric     // rows is less than the num visible rows, then make sure we show all the
4857480093f4SDimitry Andric     // rows by setting the first visible row accordingly.
4858480093f4SDimitry Andric     if (m_first_visible_row > 0 && m_num_rows < num_visible_rows)
4859480093f4SDimitry Andric       m_first_visible_row = 0;
4860480093f4SDimitry Andric 
4861480093f4SDimitry Andric     // Make sure the selected row is always visible
4862480093f4SDimitry Andric     if (m_selected_row_idx < m_first_visible_row)
4863480093f4SDimitry Andric       m_first_visible_row = m_selected_row_idx;
4864480093f4SDimitry Andric     else if (m_first_visible_row + num_visible_rows <= m_selected_row_idx)
4865480093f4SDimitry Andric       m_first_visible_row = m_selected_row_idx - num_visible_rows + 1;
4866480093f4SDimitry Andric 
4867480093f4SDimitry Andric     int row_idx = 0;
4868480093f4SDimitry Andric     int num_rows_left = num_visible_rows;
4869480093f4SDimitry Andric     m_root.Draw(window, m_first_visible_row, m_selected_row_idx, row_idx,
4870480093f4SDimitry Andric                 num_rows_left);
4871480093f4SDimitry Andric     // Get the selected row
4872480093f4SDimitry Andric     m_selected_item = m_root.GetItemForRowIndex(m_selected_row_idx);
4873480093f4SDimitry Andric 
4874480093f4SDimitry Andric     return true; // Drawing handled
4875480093f4SDimitry Andric   }
4876480093f4SDimitry Andric 
4877480093f4SDimitry Andric   const char *WindowDelegateGetHelpText() override {
4878480093f4SDimitry Andric     return "Thread window keyboard shortcuts:";
4879480093f4SDimitry Andric   }
4880480093f4SDimitry Andric 
4881480093f4SDimitry Andric   KeyHelp *WindowDelegateGetKeyHelp() override {
4882480093f4SDimitry Andric     static curses::KeyHelp g_source_view_key_help[] = {
4883480093f4SDimitry Andric         {KEY_UP, "Select previous item"},
4884480093f4SDimitry Andric         {KEY_DOWN, "Select next item"},
4885480093f4SDimitry Andric         {KEY_RIGHT, "Expand the selected item"},
4886480093f4SDimitry Andric         {KEY_LEFT,
4887480093f4SDimitry Andric          "Unexpand the selected item or select parent if not expanded"},
4888480093f4SDimitry Andric         {KEY_PPAGE, "Page up"},
4889480093f4SDimitry Andric         {KEY_NPAGE, "Page down"},
4890480093f4SDimitry Andric         {'h', "Show help dialog"},
4891480093f4SDimitry Andric         {' ', "Toggle item expansion"},
4892480093f4SDimitry Andric         {',', "Page up"},
4893480093f4SDimitry Andric         {'.', "Page down"},
4894480093f4SDimitry Andric         {'\0', nullptr}};
4895480093f4SDimitry Andric     return g_source_view_key_help;
4896480093f4SDimitry Andric   }
4897480093f4SDimitry Andric 
4898480093f4SDimitry Andric   HandleCharResult WindowDelegateHandleChar(Window &window, int c) override {
4899480093f4SDimitry Andric     switch (c) {
4900480093f4SDimitry Andric     case ',':
4901480093f4SDimitry Andric     case KEY_PPAGE:
4902480093f4SDimitry Andric       // Page up key
4903480093f4SDimitry Andric       if (m_first_visible_row > 0) {
4904480093f4SDimitry Andric         if (m_first_visible_row > m_max_y)
4905480093f4SDimitry Andric           m_first_visible_row -= m_max_y;
4906480093f4SDimitry Andric         else
4907480093f4SDimitry Andric           m_first_visible_row = 0;
4908480093f4SDimitry Andric         m_selected_row_idx = m_first_visible_row;
4909480093f4SDimitry Andric         m_selected_item = m_root.GetItemForRowIndex(m_selected_row_idx);
4910480093f4SDimitry Andric         if (m_selected_item)
4911480093f4SDimitry Andric           m_selected_item->ItemWasSelected();
4912480093f4SDimitry Andric       }
4913480093f4SDimitry Andric       return eKeyHandled;
4914480093f4SDimitry Andric 
4915480093f4SDimitry Andric     case '.':
4916480093f4SDimitry Andric     case KEY_NPAGE:
4917480093f4SDimitry Andric       // Page down key
4918480093f4SDimitry Andric       if (m_num_rows > m_max_y) {
4919480093f4SDimitry Andric         if (m_first_visible_row + m_max_y < m_num_rows) {
4920480093f4SDimitry Andric           m_first_visible_row += m_max_y;
4921480093f4SDimitry Andric           m_selected_row_idx = m_first_visible_row;
4922480093f4SDimitry Andric           m_selected_item = m_root.GetItemForRowIndex(m_selected_row_idx);
4923480093f4SDimitry Andric           if (m_selected_item)
4924480093f4SDimitry Andric             m_selected_item->ItemWasSelected();
4925480093f4SDimitry Andric         }
4926480093f4SDimitry Andric       }
4927480093f4SDimitry Andric       return eKeyHandled;
4928480093f4SDimitry Andric 
4929480093f4SDimitry Andric     case KEY_UP:
4930480093f4SDimitry Andric       if (m_selected_row_idx > 0) {
4931480093f4SDimitry Andric         --m_selected_row_idx;
4932480093f4SDimitry Andric         m_selected_item = m_root.GetItemForRowIndex(m_selected_row_idx);
4933480093f4SDimitry Andric         if (m_selected_item)
4934480093f4SDimitry Andric           m_selected_item->ItemWasSelected();
4935480093f4SDimitry Andric       }
4936480093f4SDimitry Andric       return eKeyHandled;
4937480093f4SDimitry Andric 
4938480093f4SDimitry Andric     case KEY_DOWN:
4939480093f4SDimitry Andric       if (m_selected_row_idx + 1 < m_num_rows) {
4940480093f4SDimitry Andric         ++m_selected_row_idx;
4941480093f4SDimitry Andric         m_selected_item = m_root.GetItemForRowIndex(m_selected_row_idx);
4942480093f4SDimitry Andric         if (m_selected_item)
4943480093f4SDimitry Andric           m_selected_item->ItemWasSelected();
4944480093f4SDimitry Andric       }
4945480093f4SDimitry Andric       return eKeyHandled;
4946480093f4SDimitry Andric 
4947480093f4SDimitry Andric     case KEY_RIGHT:
4948480093f4SDimitry Andric       if (m_selected_item) {
4949480093f4SDimitry Andric         if (!m_selected_item->IsExpanded())
4950480093f4SDimitry Andric           m_selected_item->Expand();
4951480093f4SDimitry Andric       }
4952480093f4SDimitry Andric       return eKeyHandled;
4953480093f4SDimitry Andric 
4954480093f4SDimitry Andric     case KEY_LEFT:
4955480093f4SDimitry Andric       if (m_selected_item) {
4956480093f4SDimitry Andric         if (m_selected_item->IsExpanded())
4957480093f4SDimitry Andric           m_selected_item->Unexpand();
4958480093f4SDimitry Andric         else if (m_selected_item->GetParent()) {
4959480093f4SDimitry Andric           m_selected_row_idx = m_selected_item->GetParent()->GetRowIndex();
4960480093f4SDimitry Andric           m_selected_item = m_root.GetItemForRowIndex(m_selected_row_idx);
4961480093f4SDimitry Andric           if (m_selected_item)
4962480093f4SDimitry Andric             m_selected_item->ItemWasSelected();
4963480093f4SDimitry Andric         }
4964480093f4SDimitry Andric       }
4965480093f4SDimitry Andric       return eKeyHandled;
4966480093f4SDimitry Andric 
4967480093f4SDimitry Andric     case ' ':
4968480093f4SDimitry Andric       // Toggle expansion state when SPACE is pressed
4969480093f4SDimitry Andric       if (m_selected_item) {
4970480093f4SDimitry Andric         if (m_selected_item->IsExpanded())
4971480093f4SDimitry Andric           m_selected_item->Unexpand();
4972480093f4SDimitry Andric         else
4973480093f4SDimitry Andric           m_selected_item->Expand();
4974480093f4SDimitry Andric       }
4975480093f4SDimitry Andric       return eKeyHandled;
4976480093f4SDimitry Andric 
4977480093f4SDimitry Andric     case 'h':
4978480093f4SDimitry Andric       window.CreateHelpSubwindow();
4979480093f4SDimitry Andric       return eKeyHandled;
4980480093f4SDimitry Andric 
4981480093f4SDimitry Andric     default:
4982480093f4SDimitry Andric       break;
4983480093f4SDimitry Andric     }
4984480093f4SDimitry Andric     return eKeyNotHandled;
4985480093f4SDimitry Andric   }
4986480093f4SDimitry Andric 
4987480093f4SDimitry Andric protected:
4988480093f4SDimitry Andric   Debugger &m_debugger;
4989480093f4SDimitry Andric   TreeDelegateSP m_delegate_sp;
4990480093f4SDimitry Andric   TreeItem m_root;
499181ad6265SDimitry Andric   TreeItem *m_selected_item = nullptr;
499281ad6265SDimitry Andric   int m_num_rows = 0;
499381ad6265SDimitry Andric   int m_selected_row_idx = 0;
499481ad6265SDimitry Andric   int m_first_visible_row = 0;
499581ad6265SDimitry Andric   int m_min_x = 0;
499681ad6265SDimitry Andric   int m_min_y = 0;
499781ad6265SDimitry Andric   int m_max_x = 0;
499881ad6265SDimitry Andric   int m_max_y = 0;
4999480093f4SDimitry Andric };
5000480093f4SDimitry Andric 
5001349cc55cSDimitry Andric // A tree delegate that just draws the text member of the tree item, it doesn't
5002349cc55cSDimitry Andric // have any children or actions.
5003349cc55cSDimitry Andric class TextTreeDelegate : public TreeDelegate {
5004349cc55cSDimitry Andric public:
5005349cc55cSDimitry Andric   TextTreeDelegate() : TreeDelegate() {}
5006349cc55cSDimitry Andric 
5007349cc55cSDimitry Andric   ~TextTreeDelegate() override = default;
5008349cc55cSDimitry Andric 
5009349cc55cSDimitry Andric   void TreeDelegateDrawTreeItem(TreeItem &item, Window &window) override {
5010349cc55cSDimitry Andric     window.PutCStringTruncated(1, item.GetText().c_str());
5011349cc55cSDimitry Andric   }
5012349cc55cSDimitry Andric 
5013349cc55cSDimitry Andric   void TreeDelegateGenerateChildren(TreeItem &item) override {}
5014349cc55cSDimitry Andric 
5015349cc55cSDimitry Andric   bool TreeDelegateItemSelected(TreeItem &item) override { return false; }
5016349cc55cSDimitry Andric };
5017349cc55cSDimitry Andric 
5018480093f4SDimitry Andric class FrameTreeDelegate : public TreeDelegate {
5019480093f4SDimitry Andric public:
5020480093f4SDimitry Andric   FrameTreeDelegate() : TreeDelegate() {
5021480093f4SDimitry Andric     FormatEntity::Parse(
502281ad6265SDimitry Andric         "#${frame.index}: {${function.name}${function.pc-offset}}}", m_format);
5023480093f4SDimitry Andric   }
5024480093f4SDimitry Andric 
5025480093f4SDimitry Andric   ~FrameTreeDelegate() override = default;
5026480093f4SDimitry Andric 
5027480093f4SDimitry Andric   void TreeDelegateDrawTreeItem(TreeItem &item, Window &window) override {
5028480093f4SDimitry Andric     Thread *thread = (Thread *)item.GetUserData();
5029480093f4SDimitry Andric     if (thread) {
5030480093f4SDimitry Andric       const uint64_t frame_idx = item.GetIdentifier();
5031480093f4SDimitry Andric       StackFrameSP frame_sp = thread->GetStackFrameAtIndex(frame_idx);
5032480093f4SDimitry Andric       if (frame_sp) {
5033480093f4SDimitry Andric         StreamString strm;
5034480093f4SDimitry Andric         const SymbolContext &sc =
5035480093f4SDimitry Andric             frame_sp->GetSymbolContext(eSymbolContextEverything);
5036480093f4SDimitry Andric         ExecutionContext exe_ctx(frame_sp);
5037480093f4SDimitry Andric         if (FormatEntity::Format(m_format, strm, &sc, &exe_ctx, nullptr,
5038480093f4SDimitry Andric                                  nullptr, false, false)) {
5039480093f4SDimitry Andric           int right_pad = 1;
5040e8d8bef9SDimitry Andric           window.PutCStringTruncated(right_pad, strm.GetString().str().c_str());
5041480093f4SDimitry Andric         }
5042480093f4SDimitry Andric       }
5043480093f4SDimitry Andric     }
5044480093f4SDimitry Andric   }
5045480093f4SDimitry Andric 
5046480093f4SDimitry Andric   void TreeDelegateGenerateChildren(TreeItem &item) override {
5047480093f4SDimitry Andric     // No children for frames yet...
5048480093f4SDimitry Andric   }
5049480093f4SDimitry Andric 
5050480093f4SDimitry Andric   bool TreeDelegateItemSelected(TreeItem &item) override {
5051480093f4SDimitry Andric     Thread *thread = (Thread *)item.GetUserData();
5052480093f4SDimitry Andric     if (thread) {
5053480093f4SDimitry Andric       thread->GetProcess()->GetThreadList().SetSelectedThreadByID(
5054480093f4SDimitry Andric           thread->GetID());
5055480093f4SDimitry Andric       const uint64_t frame_idx = item.GetIdentifier();
5056480093f4SDimitry Andric       thread->SetSelectedFrameByIndex(frame_idx);
5057480093f4SDimitry Andric       return true;
5058480093f4SDimitry Andric     }
5059480093f4SDimitry Andric     return false;
5060480093f4SDimitry Andric   }
5061480093f4SDimitry Andric 
5062480093f4SDimitry Andric protected:
5063480093f4SDimitry Andric   FormatEntity::Entry m_format;
5064480093f4SDimitry Andric };
5065480093f4SDimitry Andric 
5066480093f4SDimitry Andric class ThreadTreeDelegate : public TreeDelegate {
5067480093f4SDimitry Andric public:
5068480093f4SDimitry Andric   ThreadTreeDelegate(Debugger &debugger)
506981ad6265SDimitry Andric       : TreeDelegate(), m_debugger(debugger) {
5070480093f4SDimitry Andric     FormatEntity::Parse("thread #${thread.index}: tid = ${thread.id}{, stop "
5071480093f4SDimitry Andric                         "reason = ${thread.stop-reason}}",
5072480093f4SDimitry Andric                         m_format);
5073480093f4SDimitry Andric   }
5074480093f4SDimitry Andric 
5075480093f4SDimitry Andric   ~ThreadTreeDelegate() override = default;
5076480093f4SDimitry Andric 
5077480093f4SDimitry Andric   ProcessSP GetProcess() {
5078480093f4SDimitry Andric     return m_debugger.GetCommandInterpreter()
5079480093f4SDimitry Andric         .GetExecutionContext()
5080480093f4SDimitry Andric         .GetProcessSP();
5081480093f4SDimitry Andric   }
5082480093f4SDimitry Andric 
5083480093f4SDimitry Andric   ThreadSP GetThread(const TreeItem &item) {
5084480093f4SDimitry Andric     ProcessSP process_sp = GetProcess();
5085480093f4SDimitry Andric     if (process_sp)
5086480093f4SDimitry Andric       return process_sp->GetThreadList().FindThreadByID(item.GetIdentifier());
5087480093f4SDimitry Andric     return ThreadSP();
5088480093f4SDimitry Andric   }
5089480093f4SDimitry Andric 
5090480093f4SDimitry Andric   void TreeDelegateDrawTreeItem(TreeItem &item, Window &window) override {
5091480093f4SDimitry Andric     ThreadSP thread_sp = GetThread(item);
5092480093f4SDimitry Andric     if (thread_sp) {
5093480093f4SDimitry Andric       StreamString strm;
5094480093f4SDimitry Andric       ExecutionContext exe_ctx(thread_sp);
5095480093f4SDimitry Andric       if (FormatEntity::Format(m_format, strm, nullptr, &exe_ctx, nullptr,
5096480093f4SDimitry Andric                                nullptr, false, false)) {
5097480093f4SDimitry Andric         int right_pad = 1;
5098e8d8bef9SDimitry Andric         window.PutCStringTruncated(right_pad, strm.GetString().str().c_str());
5099480093f4SDimitry Andric       }
5100480093f4SDimitry Andric     }
5101480093f4SDimitry Andric   }
5102480093f4SDimitry Andric 
5103480093f4SDimitry Andric   void TreeDelegateGenerateChildren(TreeItem &item) override {
5104480093f4SDimitry Andric     ProcessSP process_sp = GetProcess();
5105480093f4SDimitry Andric     if (process_sp && process_sp->IsAlive()) {
5106480093f4SDimitry Andric       StateType state = process_sp->GetState();
5107480093f4SDimitry Andric       if (StateIsStoppedState(state, true)) {
5108480093f4SDimitry Andric         ThreadSP thread_sp = GetThread(item);
5109480093f4SDimitry Andric         if (thread_sp) {
5110480093f4SDimitry Andric           if (m_stop_id == process_sp->GetStopID() &&
5111480093f4SDimitry Andric               thread_sp->GetID() == m_tid)
5112480093f4SDimitry Andric             return; // Children are already up to date
5113480093f4SDimitry Andric           if (!m_frame_delegate_sp) {
5114480093f4SDimitry Andric             // Always expand the thread item the first time we show it
5115480093f4SDimitry Andric             m_frame_delegate_sp = std::make_shared<FrameTreeDelegate>();
5116480093f4SDimitry Andric           }
5117480093f4SDimitry Andric 
5118480093f4SDimitry Andric           m_stop_id = process_sp->GetStopID();
5119480093f4SDimitry Andric           m_tid = thread_sp->GetID();
5120480093f4SDimitry Andric 
5121480093f4SDimitry Andric           TreeItem t(&item, *m_frame_delegate_sp, false);
5122480093f4SDimitry Andric           size_t num_frames = thread_sp->GetStackFrameCount();
5123480093f4SDimitry Andric           item.Resize(num_frames, t);
5124480093f4SDimitry Andric           for (size_t i = 0; i < num_frames; ++i) {
5125480093f4SDimitry Andric             item[i].SetUserData(thread_sp.get());
5126480093f4SDimitry Andric             item[i].SetIdentifier(i);
5127480093f4SDimitry Andric           }
5128480093f4SDimitry Andric         }
5129480093f4SDimitry Andric         return;
5130480093f4SDimitry Andric       }
5131480093f4SDimitry Andric     }
5132480093f4SDimitry Andric     item.ClearChildren();
5133480093f4SDimitry Andric   }
5134480093f4SDimitry Andric 
5135480093f4SDimitry Andric   bool TreeDelegateItemSelected(TreeItem &item) override {
5136480093f4SDimitry Andric     ProcessSP process_sp = GetProcess();
5137480093f4SDimitry Andric     if (process_sp && process_sp->IsAlive()) {
5138480093f4SDimitry Andric       StateType state = process_sp->GetState();
5139480093f4SDimitry Andric       if (StateIsStoppedState(state, true)) {
5140480093f4SDimitry Andric         ThreadSP thread_sp = GetThread(item);
5141480093f4SDimitry Andric         if (thread_sp) {
5142480093f4SDimitry Andric           ThreadList &thread_list = thread_sp->GetProcess()->GetThreadList();
5143480093f4SDimitry Andric           std::lock_guard<std::recursive_mutex> guard(thread_list.GetMutex());
5144480093f4SDimitry Andric           ThreadSP selected_thread_sp = thread_list.GetSelectedThread();
5145480093f4SDimitry Andric           if (selected_thread_sp->GetID() != thread_sp->GetID()) {
5146480093f4SDimitry Andric             thread_list.SetSelectedThreadByID(thread_sp->GetID());
5147480093f4SDimitry Andric             return true;
5148480093f4SDimitry Andric           }
5149480093f4SDimitry Andric         }
5150480093f4SDimitry Andric       }
5151480093f4SDimitry Andric     }
5152480093f4SDimitry Andric     return false;
5153480093f4SDimitry Andric   }
5154480093f4SDimitry Andric 
5155480093f4SDimitry Andric protected:
5156480093f4SDimitry Andric   Debugger &m_debugger;
5157480093f4SDimitry Andric   std::shared_ptr<FrameTreeDelegate> m_frame_delegate_sp;
515881ad6265SDimitry Andric   lldb::user_id_t m_tid = LLDB_INVALID_THREAD_ID;
515981ad6265SDimitry Andric   uint32_t m_stop_id = UINT32_MAX;
5160480093f4SDimitry Andric   FormatEntity::Entry m_format;
5161480093f4SDimitry Andric };
5162480093f4SDimitry Andric 
5163480093f4SDimitry Andric class ThreadsTreeDelegate : public TreeDelegate {
5164480093f4SDimitry Andric public:
5165480093f4SDimitry Andric   ThreadsTreeDelegate(Debugger &debugger)
516681ad6265SDimitry Andric       : TreeDelegate(), m_thread_delegate_sp(), m_debugger(debugger) {
5167480093f4SDimitry Andric     FormatEntity::Parse("process ${process.id}{, name = ${process.name}}",
5168480093f4SDimitry Andric                         m_format);
5169480093f4SDimitry Andric   }
5170480093f4SDimitry Andric 
5171480093f4SDimitry Andric   ~ThreadsTreeDelegate() override = default;
5172480093f4SDimitry Andric 
5173480093f4SDimitry Andric   ProcessSP GetProcess() {
5174480093f4SDimitry Andric     return m_debugger.GetCommandInterpreter()
5175480093f4SDimitry Andric         .GetExecutionContext()
5176480093f4SDimitry Andric         .GetProcessSP();
5177480093f4SDimitry Andric   }
5178480093f4SDimitry Andric 
5179349cc55cSDimitry Andric   bool TreeDelegateShouldDraw() override {
5180349cc55cSDimitry Andric     ProcessSP process = GetProcess();
5181349cc55cSDimitry Andric     if (!process)
5182349cc55cSDimitry Andric       return false;
5183349cc55cSDimitry Andric 
5184349cc55cSDimitry Andric     if (StateIsRunningState(process->GetState()))
5185349cc55cSDimitry Andric       return false;
5186349cc55cSDimitry Andric 
5187349cc55cSDimitry Andric     return true;
5188349cc55cSDimitry Andric   }
5189349cc55cSDimitry Andric 
5190480093f4SDimitry Andric   void TreeDelegateDrawTreeItem(TreeItem &item, Window &window) override {
5191480093f4SDimitry Andric     ProcessSP process_sp = GetProcess();
5192480093f4SDimitry Andric     if (process_sp && process_sp->IsAlive()) {
5193480093f4SDimitry Andric       StreamString strm;
5194480093f4SDimitry Andric       ExecutionContext exe_ctx(process_sp);
5195480093f4SDimitry Andric       if (FormatEntity::Format(m_format, strm, nullptr, &exe_ctx, nullptr,
5196480093f4SDimitry Andric                                nullptr, false, false)) {
5197480093f4SDimitry Andric         int right_pad = 1;
5198e8d8bef9SDimitry Andric         window.PutCStringTruncated(right_pad, strm.GetString().str().c_str());
5199480093f4SDimitry Andric       }
5200480093f4SDimitry Andric     }
5201480093f4SDimitry Andric   }
5202480093f4SDimitry Andric 
5203480093f4SDimitry Andric   void TreeDelegateGenerateChildren(TreeItem &item) override {
5204480093f4SDimitry Andric     ProcessSP process_sp = GetProcess();
5205fe6060f1SDimitry Andric     m_update_selection = false;
5206480093f4SDimitry Andric     if (process_sp && process_sp->IsAlive()) {
5207480093f4SDimitry Andric       StateType state = process_sp->GetState();
5208480093f4SDimitry Andric       if (StateIsStoppedState(state, true)) {
5209480093f4SDimitry Andric         const uint32_t stop_id = process_sp->GetStopID();
5210480093f4SDimitry Andric         if (m_stop_id == stop_id)
5211480093f4SDimitry Andric           return; // Children are already up to date
5212480093f4SDimitry Andric 
5213480093f4SDimitry Andric         m_stop_id = stop_id;
5214fe6060f1SDimitry Andric         m_update_selection = true;
5215480093f4SDimitry Andric 
5216480093f4SDimitry Andric         if (!m_thread_delegate_sp) {
5217480093f4SDimitry Andric           // Always expand the thread item the first time we show it
5218480093f4SDimitry Andric           // item.Expand();
5219480093f4SDimitry Andric           m_thread_delegate_sp =
5220480093f4SDimitry Andric               std::make_shared<ThreadTreeDelegate>(m_debugger);
5221480093f4SDimitry Andric         }
5222480093f4SDimitry Andric 
5223480093f4SDimitry Andric         TreeItem t(&item, *m_thread_delegate_sp, false);
5224480093f4SDimitry Andric         ThreadList &threads = process_sp->GetThreadList();
5225480093f4SDimitry Andric         std::lock_guard<std::recursive_mutex> guard(threads.GetMutex());
5226fe6060f1SDimitry Andric         ThreadSP selected_thread = threads.GetSelectedThread();
5227480093f4SDimitry Andric         size_t num_threads = threads.GetSize();
5228480093f4SDimitry Andric         item.Resize(num_threads, t);
5229480093f4SDimitry Andric         for (size_t i = 0; i < num_threads; ++i) {
5230fe6060f1SDimitry Andric           ThreadSP thread = threads.GetThreadAtIndex(i);
5231fe6060f1SDimitry Andric           item[i].SetIdentifier(thread->GetID());
5232480093f4SDimitry Andric           item[i].SetMightHaveChildren(true);
5233fe6060f1SDimitry Andric           if (selected_thread->GetID() == thread->GetID())
5234fe6060f1SDimitry Andric             item[i].Expand();
5235480093f4SDimitry Andric         }
5236480093f4SDimitry Andric         return;
5237480093f4SDimitry Andric       }
5238480093f4SDimitry Andric     }
5239480093f4SDimitry Andric     item.ClearChildren();
5240480093f4SDimitry Andric   }
5241480093f4SDimitry Andric 
5242fe6060f1SDimitry Andric   void TreeDelegateUpdateSelection(TreeItem &root, int &selection_index,
5243fe6060f1SDimitry Andric                                    TreeItem *&selected_item) override {
5244fe6060f1SDimitry Andric     if (!m_update_selection)
5245fe6060f1SDimitry Andric       return;
5246fe6060f1SDimitry Andric 
5247fe6060f1SDimitry Andric     ProcessSP process_sp = GetProcess();
5248fe6060f1SDimitry Andric     if (!(process_sp && process_sp->IsAlive()))
5249fe6060f1SDimitry Andric       return;
5250fe6060f1SDimitry Andric 
5251fe6060f1SDimitry Andric     StateType state = process_sp->GetState();
5252fe6060f1SDimitry Andric     if (!StateIsStoppedState(state, true))
5253fe6060f1SDimitry Andric       return;
5254fe6060f1SDimitry Andric 
5255fe6060f1SDimitry Andric     ThreadList &threads = process_sp->GetThreadList();
5256fe6060f1SDimitry Andric     std::lock_guard<std::recursive_mutex> guard(threads.GetMutex());
5257fe6060f1SDimitry Andric     ThreadSP selected_thread = threads.GetSelectedThread();
5258fe6060f1SDimitry Andric     size_t num_threads = threads.GetSize();
5259fe6060f1SDimitry Andric     for (size_t i = 0; i < num_threads; ++i) {
5260fe6060f1SDimitry Andric       ThreadSP thread = threads.GetThreadAtIndex(i);
5261fe6060f1SDimitry Andric       if (selected_thread->GetID() == thread->GetID()) {
5262*06c3fb27SDimitry Andric         selected_item =
5263*06c3fb27SDimitry Andric             &root[i][thread->GetSelectedFrameIndex(SelectMostRelevantFrame)];
5264fe6060f1SDimitry Andric         selection_index = selected_item->GetRowIndex();
5265fe6060f1SDimitry Andric         return;
5266fe6060f1SDimitry Andric       }
5267fe6060f1SDimitry Andric     }
5268fe6060f1SDimitry Andric   }
5269fe6060f1SDimitry Andric 
5270480093f4SDimitry Andric   bool TreeDelegateItemSelected(TreeItem &item) override { return false; }
5271480093f4SDimitry Andric 
5272fe6060f1SDimitry Andric   bool TreeDelegateExpandRootByDefault() override { return true; }
5273fe6060f1SDimitry Andric 
5274480093f4SDimitry Andric protected:
5275480093f4SDimitry Andric   std::shared_ptr<ThreadTreeDelegate> m_thread_delegate_sp;
5276480093f4SDimitry Andric   Debugger &m_debugger;
527781ad6265SDimitry Andric   uint32_t m_stop_id = UINT32_MAX;
527881ad6265SDimitry Andric   bool m_update_selection = false;
5279480093f4SDimitry Andric   FormatEntity::Entry m_format;
5280480093f4SDimitry Andric };
5281480093f4SDimitry Andric 
5282349cc55cSDimitry Andric class BreakpointLocationTreeDelegate : public TreeDelegate {
5283349cc55cSDimitry Andric public:
5284349cc55cSDimitry Andric   BreakpointLocationTreeDelegate(Debugger &debugger)
5285349cc55cSDimitry Andric       : TreeDelegate(), m_debugger(debugger) {}
5286349cc55cSDimitry Andric 
5287349cc55cSDimitry Andric   ~BreakpointLocationTreeDelegate() override = default;
5288349cc55cSDimitry Andric 
5289349cc55cSDimitry Andric   Process *GetProcess() {
5290349cc55cSDimitry Andric     ExecutionContext exe_ctx(
5291349cc55cSDimitry Andric         m_debugger.GetCommandInterpreter().GetExecutionContext());
5292349cc55cSDimitry Andric     return exe_ctx.GetProcessPtr();
5293349cc55cSDimitry Andric   }
5294349cc55cSDimitry Andric 
5295349cc55cSDimitry Andric   BreakpointLocationSP GetBreakpointLocation(const TreeItem &item) {
5296349cc55cSDimitry Andric     Breakpoint *breakpoint = (Breakpoint *)item.GetUserData();
5297349cc55cSDimitry Andric     return breakpoint->GetLocationAtIndex(item.GetIdentifier());
5298349cc55cSDimitry Andric   }
5299349cc55cSDimitry Andric 
5300349cc55cSDimitry Andric   void TreeDelegateDrawTreeItem(TreeItem &item, Window &window) override {
5301349cc55cSDimitry Andric     BreakpointLocationSP breakpoint_location = GetBreakpointLocation(item);
5302349cc55cSDimitry Andric     Process *process = GetProcess();
5303349cc55cSDimitry Andric     StreamString stream;
5304349cc55cSDimitry Andric     stream.Printf("%i.%i: ", breakpoint_location->GetBreakpoint().GetID(),
5305349cc55cSDimitry Andric                   breakpoint_location->GetID());
5306349cc55cSDimitry Andric     Address address = breakpoint_location->GetAddress();
5307349cc55cSDimitry Andric     address.Dump(&stream, process, Address::DumpStyleResolvedDescription,
5308349cc55cSDimitry Andric                  Address::DumpStyleInvalid);
5309349cc55cSDimitry Andric     window.PutCStringTruncated(1, stream.GetString().str().c_str());
5310349cc55cSDimitry Andric   }
5311349cc55cSDimitry Andric 
5312349cc55cSDimitry Andric   StringList ComputeDetailsList(BreakpointLocationSP breakpoint_location) {
5313349cc55cSDimitry Andric     StringList details;
5314349cc55cSDimitry Andric 
5315349cc55cSDimitry Andric     Address address = breakpoint_location->GetAddress();
5316349cc55cSDimitry Andric     SymbolContext symbol_context;
5317349cc55cSDimitry Andric     address.CalculateSymbolContext(&symbol_context);
5318349cc55cSDimitry Andric 
5319349cc55cSDimitry Andric     if (symbol_context.module_sp) {
5320349cc55cSDimitry Andric       StreamString module_stream;
5321349cc55cSDimitry Andric       module_stream.PutCString("module = ");
5322349cc55cSDimitry Andric       symbol_context.module_sp->GetFileSpec().Dump(
5323349cc55cSDimitry Andric           module_stream.AsRawOstream());
5324349cc55cSDimitry Andric       details.AppendString(module_stream.GetString());
5325349cc55cSDimitry Andric     }
5326349cc55cSDimitry Andric 
5327349cc55cSDimitry Andric     if (symbol_context.comp_unit != nullptr) {
5328349cc55cSDimitry Andric       StreamString compile_unit_stream;
5329349cc55cSDimitry Andric       compile_unit_stream.PutCString("compile unit = ");
5330349cc55cSDimitry Andric       symbol_context.comp_unit->GetPrimaryFile().GetFilename().Dump(
5331349cc55cSDimitry Andric           &compile_unit_stream);
5332349cc55cSDimitry Andric       details.AppendString(compile_unit_stream.GetString());
5333349cc55cSDimitry Andric 
5334349cc55cSDimitry Andric       if (symbol_context.function != nullptr) {
5335349cc55cSDimitry Andric         StreamString function_stream;
5336349cc55cSDimitry Andric         function_stream.PutCString("function = ");
5337349cc55cSDimitry Andric         function_stream.PutCString(
5338349cc55cSDimitry Andric             symbol_context.function->GetName().AsCString("<unknown>"));
5339349cc55cSDimitry Andric         details.AppendString(function_stream.GetString());
5340349cc55cSDimitry Andric       }
5341349cc55cSDimitry Andric 
5342349cc55cSDimitry Andric       if (symbol_context.line_entry.line > 0) {
5343349cc55cSDimitry Andric         StreamString location_stream;
5344349cc55cSDimitry Andric         location_stream.PutCString("location = ");
5345349cc55cSDimitry Andric         symbol_context.line_entry.DumpStopContext(&location_stream, true);
5346349cc55cSDimitry Andric         details.AppendString(location_stream.GetString());
5347349cc55cSDimitry Andric       }
5348349cc55cSDimitry Andric 
5349349cc55cSDimitry Andric     } else {
5350349cc55cSDimitry Andric       if (symbol_context.symbol) {
5351349cc55cSDimitry Andric         StreamString symbol_stream;
5352349cc55cSDimitry Andric         if (breakpoint_location->IsReExported())
5353349cc55cSDimitry Andric           symbol_stream.PutCString("re-exported target = ");
5354349cc55cSDimitry Andric         else
5355349cc55cSDimitry Andric           symbol_stream.PutCString("symbol = ");
5356349cc55cSDimitry Andric         symbol_stream.PutCString(
5357349cc55cSDimitry Andric             symbol_context.symbol->GetName().AsCString("<unknown>"));
5358349cc55cSDimitry Andric         details.AppendString(symbol_stream.GetString());
5359349cc55cSDimitry Andric       }
5360349cc55cSDimitry Andric     }
5361349cc55cSDimitry Andric 
5362349cc55cSDimitry Andric     Process *process = GetProcess();
5363349cc55cSDimitry Andric 
5364349cc55cSDimitry Andric     StreamString address_stream;
5365349cc55cSDimitry Andric     address.Dump(&address_stream, process, Address::DumpStyleLoadAddress,
5366349cc55cSDimitry Andric                  Address::DumpStyleModuleWithFileAddress);
5367349cc55cSDimitry Andric     details.AppendString(address_stream.GetString());
5368349cc55cSDimitry Andric 
5369349cc55cSDimitry Andric     BreakpointSiteSP breakpoint_site = breakpoint_location->GetBreakpointSite();
5370349cc55cSDimitry Andric     if (breakpoint_location->IsIndirect() && breakpoint_site) {
5371349cc55cSDimitry Andric       Address resolved_address;
5372349cc55cSDimitry Andric       resolved_address.SetLoadAddress(breakpoint_site->GetLoadAddress(),
5373349cc55cSDimitry Andric                                       &breakpoint_location->GetTarget());
5374349cc55cSDimitry Andric       Symbol *resolved_symbol = resolved_address.CalculateSymbolContextSymbol();
5375349cc55cSDimitry Andric       if (resolved_symbol) {
5376349cc55cSDimitry Andric         StreamString indirect_target_stream;
5377349cc55cSDimitry Andric         indirect_target_stream.PutCString("indirect target = ");
5378349cc55cSDimitry Andric         indirect_target_stream.PutCString(
5379349cc55cSDimitry Andric             resolved_symbol->GetName().GetCString());
5380349cc55cSDimitry Andric         details.AppendString(indirect_target_stream.GetString());
5381349cc55cSDimitry Andric       }
5382349cc55cSDimitry Andric     }
5383349cc55cSDimitry Andric 
5384349cc55cSDimitry Andric     bool is_resolved = breakpoint_location->IsResolved();
5385349cc55cSDimitry Andric     StreamString resolved_stream;
5386349cc55cSDimitry Andric     resolved_stream.Printf("resolved = %s", is_resolved ? "true" : "false");
5387349cc55cSDimitry Andric     details.AppendString(resolved_stream.GetString());
5388349cc55cSDimitry Andric 
5389349cc55cSDimitry Andric     bool is_hardware = is_resolved && breakpoint_site->IsHardware();
5390349cc55cSDimitry Andric     StreamString hardware_stream;
5391349cc55cSDimitry Andric     hardware_stream.Printf("hardware = %s", is_hardware ? "true" : "false");
5392349cc55cSDimitry Andric     details.AppendString(hardware_stream.GetString());
5393349cc55cSDimitry Andric 
5394349cc55cSDimitry Andric     StreamString hit_count_stream;
5395349cc55cSDimitry Andric     hit_count_stream.Printf("hit count = %-4u",
5396349cc55cSDimitry Andric                             breakpoint_location->GetHitCount());
5397349cc55cSDimitry Andric     details.AppendString(hit_count_stream.GetString());
5398349cc55cSDimitry Andric 
5399349cc55cSDimitry Andric     return details;
5400349cc55cSDimitry Andric   }
5401349cc55cSDimitry Andric 
5402349cc55cSDimitry Andric   void TreeDelegateGenerateChildren(TreeItem &item) override {
5403349cc55cSDimitry Andric     BreakpointLocationSP breakpoint_location = GetBreakpointLocation(item);
5404349cc55cSDimitry Andric     StringList details = ComputeDetailsList(breakpoint_location);
5405349cc55cSDimitry Andric 
5406349cc55cSDimitry Andric     if (!m_string_delegate_sp)
5407349cc55cSDimitry Andric       m_string_delegate_sp = std::make_shared<TextTreeDelegate>();
5408349cc55cSDimitry Andric     TreeItem details_tree_item(&item, *m_string_delegate_sp, false);
5409349cc55cSDimitry Andric 
5410349cc55cSDimitry Andric     item.Resize(details.GetSize(), details_tree_item);
5411349cc55cSDimitry Andric     for (size_t i = 0; i < details.GetSize(); i++) {
5412349cc55cSDimitry Andric       item[i].SetText(details.GetStringAtIndex(i));
5413349cc55cSDimitry Andric     }
5414349cc55cSDimitry Andric   }
5415349cc55cSDimitry Andric 
5416349cc55cSDimitry Andric   bool TreeDelegateItemSelected(TreeItem &item) override { return false; }
5417349cc55cSDimitry Andric 
5418349cc55cSDimitry Andric protected:
5419349cc55cSDimitry Andric   Debugger &m_debugger;
5420349cc55cSDimitry Andric   std::shared_ptr<TextTreeDelegate> m_string_delegate_sp;
5421349cc55cSDimitry Andric };
5422349cc55cSDimitry Andric 
5423349cc55cSDimitry Andric class BreakpointTreeDelegate : public TreeDelegate {
5424349cc55cSDimitry Andric public:
5425349cc55cSDimitry Andric   BreakpointTreeDelegate(Debugger &debugger)
5426349cc55cSDimitry Andric       : TreeDelegate(), m_debugger(debugger),
5427349cc55cSDimitry Andric         m_breakpoint_location_delegate_sp() {}
5428349cc55cSDimitry Andric 
5429349cc55cSDimitry Andric   ~BreakpointTreeDelegate() override = default;
5430349cc55cSDimitry Andric 
5431349cc55cSDimitry Andric   BreakpointSP GetBreakpoint(const TreeItem &item) {
5432349cc55cSDimitry Andric     TargetSP target = m_debugger.GetSelectedTarget();
5433349cc55cSDimitry Andric     BreakpointList &breakpoints = target->GetBreakpointList(false);
5434349cc55cSDimitry Andric     return breakpoints.GetBreakpointAtIndex(item.GetIdentifier());
5435349cc55cSDimitry Andric   }
5436349cc55cSDimitry Andric 
5437349cc55cSDimitry Andric   void TreeDelegateDrawTreeItem(TreeItem &item, Window &window) override {
5438349cc55cSDimitry Andric     BreakpointSP breakpoint = GetBreakpoint(item);
5439349cc55cSDimitry Andric     StreamString stream;
5440349cc55cSDimitry Andric     stream.Format("{0}: ", breakpoint->GetID());
5441349cc55cSDimitry Andric     breakpoint->GetResolverDescription(&stream);
5442349cc55cSDimitry Andric     breakpoint->GetFilterDescription(&stream);
5443349cc55cSDimitry Andric     window.PutCStringTruncated(1, stream.GetString().str().c_str());
5444349cc55cSDimitry Andric   }
5445349cc55cSDimitry Andric 
5446349cc55cSDimitry Andric   void TreeDelegateGenerateChildren(TreeItem &item) override {
5447349cc55cSDimitry Andric     BreakpointSP breakpoint = GetBreakpoint(item);
5448349cc55cSDimitry Andric 
5449349cc55cSDimitry Andric     if (!m_breakpoint_location_delegate_sp)
5450349cc55cSDimitry Andric       m_breakpoint_location_delegate_sp =
5451349cc55cSDimitry Andric           std::make_shared<BreakpointLocationTreeDelegate>(m_debugger);
5452349cc55cSDimitry Andric     TreeItem breakpoint_location_tree_item(
5453349cc55cSDimitry Andric         &item, *m_breakpoint_location_delegate_sp, true);
5454349cc55cSDimitry Andric 
5455349cc55cSDimitry Andric     item.Resize(breakpoint->GetNumLocations(), breakpoint_location_tree_item);
5456349cc55cSDimitry Andric     for (size_t i = 0; i < breakpoint->GetNumLocations(); i++) {
5457349cc55cSDimitry Andric       item[i].SetIdentifier(i);
5458349cc55cSDimitry Andric       item[i].SetUserData(breakpoint.get());
5459349cc55cSDimitry Andric     }
5460349cc55cSDimitry Andric   }
5461349cc55cSDimitry Andric 
5462349cc55cSDimitry Andric   bool TreeDelegateItemSelected(TreeItem &item) override { return false; }
5463349cc55cSDimitry Andric 
5464349cc55cSDimitry Andric protected:
5465349cc55cSDimitry Andric   Debugger &m_debugger;
5466349cc55cSDimitry Andric   std::shared_ptr<BreakpointLocationTreeDelegate>
5467349cc55cSDimitry Andric       m_breakpoint_location_delegate_sp;
5468349cc55cSDimitry Andric };
5469349cc55cSDimitry Andric 
5470349cc55cSDimitry Andric class BreakpointsTreeDelegate : public TreeDelegate {
5471349cc55cSDimitry Andric public:
5472349cc55cSDimitry Andric   BreakpointsTreeDelegate(Debugger &debugger)
5473349cc55cSDimitry Andric       : TreeDelegate(), m_debugger(debugger), m_breakpoint_delegate_sp() {}
5474349cc55cSDimitry Andric 
5475349cc55cSDimitry Andric   ~BreakpointsTreeDelegate() override = default;
5476349cc55cSDimitry Andric 
5477349cc55cSDimitry Andric   bool TreeDelegateShouldDraw() override {
5478349cc55cSDimitry Andric     TargetSP target = m_debugger.GetSelectedTarget();
5479349cc55cSDimitry Andric     if (!target)
5480349cc55cSDimitry Andric       return false;
5481349cc55cSDimitry Andric 
5482349cc55cSDimitry Andric     return true;
5483349cc55cSDimitry Andric   }
5484349cc55cSDimitry Andric 
5485349cc55cSDimitry Andric   void TreeDelegateDrawTreeItem(TreeItem &item, Window &window) override {
5486349cc55cSDimitry Andric     window.PutCString("Breakpoints");
5487349cc55cSDimitry Andric   }
5488349cc55cSDimitry Andric 
5489349cc55cSDimitry Andric   void TreeDelegateGenerateChildren(TreeItem &item) override {
5490349cc55cSDimitry Andric     TargetSP target = m_debugger.GetSelectedTarget();
5491349cc55cSDimitry Andric 
5492349cc55cSDimitry Andric     BreakpointList &breakpoints = target->GetBreakpointList(false);
5493349cc55cSDimitry Andric     std::unique_lock<std::recursive_mutex> lock;
5494349cc55cSDimitry Andric     breakpoints.GetListMutex(lock);
5495349cc55cSDimitry Andric 
5496349cc55cSDimitry Andric     if (!m_breakpoint_delegate_sp)
5497349cc55cSDimitry Andric       m_breakpoint_delegate_sp =
5498349cc55cSDimitry Andric           std::make_shared<BreakpointTreeDelegate>(m_debugger);
5499349cc55cSDimitry Andric     TreeItem breakpoint_tree_item(&item, *m_breakpoint_delegate_sp, true);
5500349cc55cSDimitry Andric 
5501349cc55cSDimitry Andric     item.Resize(breakpoints.GetSize(), breakpoint_tree_item);
5502349cc55cSDimitry Andric     for (size_t i = 0; i < breakpoints.GetSize(); i++) {
5503349cc55cSDimitry Andric       item[i].SetIdentifier(i);
5504349cc55cSDimitry Andric     }
5505349cc55cSDimitry Andric   }
5506349cc55cSDimitry Andric 
5507349cc55cSDimitry Andric   bool TreeDelegateItemSelected(TreeItem &item) override { return false; }
5508349cc55cSDimitry Andric 
5509349cc55cSDimitry Andric   bool TreeDelegateExpandRootByDefault() override { return true; }
5510349cc55cSDimitry Andric 
5511349cc55cSDimitry Andric protected:
5512349cc55cSDimitry Andric   Debugger &m_debugger;
5513349cc55cSDimitry Andric   std::shared_ptr<BreakpointTreeDelegate> m_breakpoint_delegate_sp;
5514349cc55cSDimitry Andric };
5515349cc55cSDimitry Andric 
5516480093f4SDimitry Andric class ValueObjectListDelegate : public WindowDelegate {
5517480093f4SDimitry Andric public:
5518fe6060f1SDimitry Andric   ValueObjectListDelegate() : m_rows() {}
5519480093f4SDimitry Andric 
552081ad6265SDimitry Andric   ValueObjectListDelegate(ValueObjectList &valobj_list) : m_rows() {
5521480093f4SDimitry Andric     SetValues(valobj_list);
5522480093f4SDimitry Andric   }
5523480093f4SDimitry Andric 
5524480093f4SDimitry Andric   ~ValueObjectListDelegate() override = default;
5525480093f4SDimitry Andric 
5526480093f4SDimitry Andric   void SetValues(ValueObjectList &valobj_list) {
5527480093f4SDimitry Andric     m_selected_row = nullptr;
5528480093f4SDimitry Andric     m_selected_row_idx = 0;
5529480093f4SDimitry Andric     m_first_visible_row = 0;
5530480093f4SDimitry Andric     m_num_rows = 0;
5531480093f4SDimitry Andric     m_rows.clear();
5532480093f4SDimitry Andric     for (auto &valobj_sp : valobj_list.GetObjects())
5533480093f4SDimitry Andric       m_rows.push_back(Row(valobj_sp, nullptr));
5534480093f4SDimitry Andric   }
5535480093f4SDimitry Andric 
5536480093f4SDimitry Andric   bool WindowDelegateDraw(Window &window, bool force) override {
5537480093f4SDimitry Andric     m_num_rows = 0;
5538480093f4SDimitry Andric     m_min_x = 2;
5539480093f4SDimitry Andric     m_min_y = 1;
5540480093f4SDimitry Andric     m_max_x = window.GetWidth() - 1;
5541480093f4SDimitry Andric     m_max_y = window.GetHeight() - 1;
5542480093f4SDimitry Andric 
5543480093f4SDimitry Andric     window.Erase();
5544480093f4SDimitry Andric     window.DrawTitleBox(window.GetName());
5545480093f4SDimitry Andric 
5546480093f4SDimitry Andric     const int num_visible_rows = NumVisibleRows();
5547480093f4SDimitry Andric     const int num_rows = CalculateTotalNumberRows(m_rows);
5548480093f4SDimitry Andric 
5549480093f4SDimitry Andric     // If we unexpanded while having something selected our total number of
5550480093f4SDimitry Andric     // rows is less than the num visible rows, then make sure we show all the
5551480093f4SDimitry Andric     // rows by setting the first visible row accordingly.
5552480093f4SDimitry Andric     if (m_first_visible_row > 0 && num_rows < num_visible_rows)
5553480093f4SDimitry Andric       m_first_visible_row = 0;
5554480093f4SDimitry Andric 
5555480093f4SDimitry Andric     // Make sure the selected row is always visible
5556480093f4SDimitry Andric     if (m_selected_row_idx < m_first_visible_row)
5557480093f4SDimitry Andric       m_first_visible_row = m_selected_row_idx;
5558480093f4SDimitry Andric     else if (m_first_visible_row + num_visible_rows <= m_selected_row_idx)
5559480093f4SDimitry Andric       m_first_visible_row = m_selected_row_idx - num_visible_rows + 1;
5560480093f4SDimitry Andric 
5561480093f4SDimitry Andric     DisplayRows(window, m_rows, g_options);
5562480093f4SDimitry Andric 
5563480093f4SDimitry Andric     // Get the selected row
5564480093f4SDimitry Andric     m_selected_row = GetRowForRowIndex(m_selected_row_idx);
5565480093f4SDimitry Andric     // Keep the cursor on the selected row so the highlight and the cursor are
5566480093f4SDimitry Andric     // always on the same line
5567480093f4SDimitry Andric     if (m_selected_row)
5568480093f4SDimitry Andric       window.MoveCursor(m_selected_row->x, m_selected_row->y);
5569480093f4SDimitry Andric 
5570480093f4SDimitry Andric     return true; // Drawing handled
5571480093f4SDimitry Andric   }
5572480093f4SDimitry Andric 
5573480093f4SDimitry Andric   KeyHelp *WindowDelegateGetKeyHelp() override {
5574480093f4SDimitry Andric     static curses::KeyHelp g_source_view_key_help[] = {
5575480093f4SDimitry Andric         {KEY_UP, "Select previous item"},
5576480093f4SDimitry Andric         {KEY_DOWN, "Select next item"},
5577480093f4SDimitry Andric         {KEY_RIGHT, "Expand selected item"},
5578480093f4SDimitry Andric         {KEY_LEFT, "Unexpand selected item or select parent if not expanded"},
5579480093f4SDimitry Andric         {KEY_PPAGE, "Page up"},
5580480093f4SDimitry Andric         {KEY_NPAGE, "Page down"},
5581480093f4SDimitry Andric         {'A', "Format as annotated address"},
5582480093f4SDimitry Andric         {'b', "Format as binary"},
5583480093f4SDimitry Andric         {'B', "Format as hex bytes with ASCII"},
5584480093f4SDimitry Andric         {'c', "Format as character"},
5585480093f4SDimitry Andric         {'d', "Format as a signed integer"},
5586480093f4SDimitry Andric         {'D', "Format selected value using the default format for the type"},
5587480093f4SDimitry Andric         {'f', "Format as float"},
5588480093f4SDimitry Andric         {'h', "Show help dialog"},
5589480093f4SDimitry Andric         {'i', "Format as instructions"},
5590480093f4SDimitry Andric         {'o', "Format as octal"},
5591480093f4SDimitry Andric         {'p', "Format as pointer"},
5592480093f4SDimitry Andric         {'s', "Format as C string"},
5593480093f4SDimitry Andric         {'t', "Toggle showing/hiding type names"},
5594480093f4SDimitry Andric         {'u', "Format as an unsigned integer"},
5595480093f4SDimitry Andric         {'x', "Format as hex"},
5596480093f4SDimitry Andric         {'X', "Format as uppercase hex"},
5597480093f4SDimitry Andric         {' ', "Toggle item expansion"},
5598480093f4SDimitry Andric         {',', "Page up"},
5599480093f4SDimitry Andric         {'.', "Page down"},
5600480093f4SDimitry Andric         {'\0', nullptr}};
5601480093f4SDimitry Andric     return g_source_view_key_help;
5602480093f4SDimitry Andric   }
5603480093f4SDimitry Andric 
5604480093f4SDimitry Andric   HandleCharResult WindowDelegateHandleChar(Window &window, int c) override {
5605480093f4SDimitry Andric     switch (c) {
5606480093f4SDimitry Andric     case 'x':
5607480093f4SDimitry Andric     case 'X':
5608480093f4SDimitry Andric     case 'o':
5609480093f4SDimitry Andric     case 's':
5610480093f4SDimitry Andric     case 'u':
5611480093f4SDimitry Andric     case 'd':
5612480093f4SDimitry Andric     case 'D':
5613480093f4SDimitry Andric     case 'i':
5614480093f4SDimitry Andric     case 'A':
5615480093f4SDimitry Andric     case 'p':
5616480093f4SDimitry Andric     case 'c':
5617480093f4SDimitry Andric     case 'b':
5618480093f4SDimitry Andric     case 'B':
5619480093f4SDimitry Andric     case 'f':
5620480093f4SDimitry Andric       // Change the format for the currently selected item
5621480093f4SDimitry Andric       if (m_selected_row) {
5622480093f4SDimitry Andric         auto valobj_sp = m_selected_row->value.GetSP();
5623480093f4SDimitry Andric         if (valobj_sp)
5624480093f4SDimitry Andric           valobj_sp->SetFormat(FormatForChar(c));
5625480093f4SDimitry Andric       }
5626480093f4SDimitry Andric       return eKeyHandled;
5627480093f4SDimitry Andric 
5628480093f4SDimitry Andric     case 't':
5629480093f4SDimitry Andric       // Toggle showing type names
5630480093f4SDimitry Andric       g_options.show_types = !g_options.show_types;
5631480093f4SDimitry Andric       return eKeyHandled;
5632480093f4SDimitry Andric 
5633480093f4SDimitry Andric     case ',':
5634480093f4SDimitry Andric     case KEY_PPAGE:
5635480093f4SDimitry Andric       // Page up key
5636480093f4SDimitry Andric       if (m_first_visible_row > 0) {
5637480093f4SDimitry Andric         if (static_cast<int>(m_first_visible_row) > m_max_y)
5638480093f4SDimitry Andric           m_first_visible_row -= m_max_y;
5639480093f4SDimitry Andric         else
5640480093f4SDimitry Andric           m_first_visible_row = 0;
5641480093f4SDimitry Andric         m_selected_row_idx = m_first_visible_row;
5642480093f4SDimitry Andric       }
5643480093f4SDimitry Andric       return eKeyHandled;
5644480093f4SDimitry Andric 
5645480093f4SDimitry Andric     case '.':
5646480093f4SDimitry Andric     case KEY_NPAGE:
5647480093f4SDimitry Andric       // Page down key
5648480093f4SDimitry Andric       if (m_num_rows > static_cast<size_t>(m_max_y)) {
5649480093f4SDimitry Andric         if (m_first_visible_row + m_max_y < m_num_rows) {
5650480093f4SDimitry Andric           m_first_visible_row += m_max_y;
5651480093f4SDimitry Andric           m_selected_row_idx = m_first_visible_row;
5652480093f4SDimitry Andric         }
5653480093f4SDimitry Andric       }
5654480093f4SDimitry Andric       return eKeyHandled;
5655480093f4SDimitry Andric 
5656480093f4SDimitry Andric     case KEY_UP:
5657480093f4SDimitry Andric       if (m_selected_row_idx > 0)
5658480093f4SDimitry Andric         --m_selected_row_idx;
5659480093f4SDimitry Andric       return eKeyHandled;
5660480093f4SDimitry Andric 
5661480093f4SDimitry Andric     case KEY_DOWN:
5662480093f4SDimitry Andric       if (m_selected_row_idx + 1 < m_num_rows)
5663480093f4SDimitry Andric         ++m_selected_row_idx;
5664480093f4SDimitry Andric       return eKeyHandled;
5665480093f4SDimitry Andric 
5666480093f4SDimitry Andric     case KEY_RIGHT:
5667480093f4SDimitry Andric       if (m_selected_row) {
5668480093f4SDimitry Andric         if (!m_selected_row->expanded)
5669480093f4SDimitry Andric           m_selected_row->Expand();
5670480093f4SDimitry Andric       }
5671480093f4SDimitry Andric       return eKeyHandled;
5672480093f4SDimitry Andric 
5673480093f4SDimitry Andric     case KEY_LEFT:
5674480093f4SDimitry Andric       if (m_selected_row) {
5675480093f4SDimitry Andric         if (m_selected_row->expanded)
5676480093f4SDimitry Andric           m_selected_row->Unexpand();
5677480093f4SDimitry Andric         else if (m_selected_row->parent)
5678480093f4SDimitry Andric           m_selected_row_idx = m_selected_row->parent->row_idx;
5679480093f4SDimitry Andric       }
5680480093f4SDimitry Andric       return eKeyHandled;
5681480093f4SDimitry Andric 
5682480093f4SDimitry Andric     case ' ':
5683480093f4SDimitry Andric       // Toggle expansion state when SPACE is pressed
5684480093f4SDimitry Andric       if (m_selected_row) {
5685480093f4SDimitry Andric         if (m_selected_row->expanded)
5686480093f4SDimitry Andric           m_selected_row->Unexpand();
5687480093f4SDimitry Andric         else
5688480093f4SDimitry Andric           m_selected_row->Expand();
5689480093f4SDimitry Andric       }
5690480093f4SDimitry Andric       return eKeyHandled;
5691480093f4SDimitry Andric 
5692480093f4SDimitry Andric     case 'h':
5693480093f4SDimitry Andric       window.CreateHelpSubwindow();
5694480093f4SDimitry Andric       return eKeyHandled;
5695480093f4SDimitry Andric 
5696480093f4SDimitry Andric     default:
5697480093f4SDimitry Andric       break;
5698480093f4SDimitry Andric     }
5699480093f4SDimitry Andric     return eKeyNotHandled;
5700480093f4SDimitry Andric   }
5701480093f4SDimitry Andric 
5702480093f4SDimitry Andric protected:
5703480093f4SDimitry Andric   std::vector<Row> m_rows;
5704fe6060f1SDimitry Andric   Row *m_selected_row = nullptr;
5705fe6060f1SDimitry Andric   uint32_t m_selected_row_idx = 0;
5706fe6060f1SDimitry Andric   uint32_t m_first_visible_row = 0;
5707fe6060f1SDimitry Andric   uint32_t m_num_rows = 0;
5708bdd1243dSDimitry Andric   int m_min_x = 0;
5709bdd1243dSDimitry Andric   int m_min_y = 0;
5710fe6060f1SDimitry Andric   int m_max_x = 0;
5711fe6060f1SDimitry Andric   int m_max_y = 0;
5712480093f4SDimitry Andric 
5713480093f4SDimitry Andric   static Format FormatForChar(int c) {
5714480093f4SDimitry Andric     switch (c) {
5715480093f4SDimitry Andric     case 'x':
5716480093f4SDimitry Andric       return eFormatHex;
5717480093f4SDimitry Andric     case 'X':
5718480093f4SDimitry Andric       return eFormatHexUppercase;
5719480093f4SDimitry Andric     case 'o':
5720480093f4SDimitry Andric       return eFormatOctal;
5721480093f4SDimitry Andric     case 's':
5722480093f4SDimitry Andric       return eFormatCString;
5723480093f4SDimitry Andric     case 'u':
5724480093f4SDimitry Andric       return eFormatUnsigned;
5725480093f4SDimitry Andric     case 'd':
5726480093f4SDimitry Andric       return eFormatDecimal;
5727480093f4SDimitry Andric     case 'D':
5728480093f4SDimitry Andric       return eFormatDefault;
5729480093f4SDimitry Andric     case 'i':
5730480093f4SDimitry Andric       return eFormatInstruction;
5731480093f4SDimitry Andric     case 'A':
5732480093f4SDimitry Andric       return eFormatAddressInfo;
5733480093f4SDimitry Andric     case 'p':
5734480093f4SDimitry Andric       return eFormatPointer;
5735480093f4SDimitry Andric     case 'c':
5736480093f4SDimitry Andric       return eFormatChar;
5737480093f4SDimitry Andric     case 'b':
5738480093f4SDimitry Andric       return eFormatBinary;
5739480093f4SDimitry Andric     case 'B':
5740480093f4SDimitry Andric       return eFormatBytesWithASCII;
5741480093f4SDimitry Andric     case 'f':
5742480093f4SDimitry Andric       return eFormatFloat;
5743480093f4SDimitry Andric     }
5744480093f4SDimitry Andric     return eFormatDefault;
5745480093f4SDimitry Andric   }
5746480093f4SDimitry Andric 
5747480093f4SDimitry Andric   bool DisplayRowObject(Window &window, Row &row, DisplayOptions &options,
5748480093f4SDimitry Andric                         bool highlight, bool last_child) {
5749480093f4SDimitry Andric     ValueObject *valobj = row.value.GetSP().get();
5750480093f4SDimitry Andric 
5751480093f4SDimitry Andric     if (valobj == nullptr)
5752480093f4SDimitry Andric       return false;
5753480093f4SDimitry Andric 
5754480093f4SDimitry Andric     const char *type_name =
5755480093f4SDimitry Andric         options.show_types ? valobj->GetTypeName().GetCString() : nullptr;
5756480093f4SDimitry Andric     const char *name = valobj->GetName().GetCString();
5757480093f4SDimitry Andric     const char *value = valobj->GetValueAsCString();
5758480093f4SDimitry Andric     const char *summary = valobj->GetSummaryAsCString();
5759480093f4SDimitry Andric 
5760480093f4SDimitry Andric     window.MoveCursor(row.x, row.y);
5761480093f4SDimitry Andric 
5762480093f4SDimitry Andric     row.DrawTree(window);
5763480093f4SDimitry Andric 
5764480093f4SDimitry Andric     if (highlight)
5765480093f4SDimitry Andric       window.AttributeOn(A_REVERSE);
5766480093f4SDimitry Andric 
5767480093f4SDimitry Andric     if (type_name && type_name[0])
5768e8d8bef9SDimitry Andric       window.PrintfTruncated(1, "(%s) ", type_name);
5769480093f4SDimitry Andric 
5770480093f4SDimitry Andric     if (name && name[0])
5771e8d8bef9SDimitry Andric       window.PutCStringTruncated(1, name);
5772480093f4SDimitry Andric 
5773480093f4SDimitry Andric     attr_t changd_attr = 0;
5774480093f4SDimitry Andric     if (valobj->GetValueDidChange())
5775e8d8bef9SDimitry Andric       changd_attr = COLOR_PAIR(RedOnBlack) | A_BOLD;
5776480093f4SDimitry Andric 
5777480093f4SDimitry Andric     if (value && value[0]) {
5778e8d8bef9SDimitry Andric       window.PutCStringTruncated(1, " = ");
5779480093f4SDimitry Andric       if (changd_attr)
5780480093f4SDimitry Andric         window.AttributeOn(changd_attr);
5781e8d8bef9SDimitry Andric       window.PutCStringTruncated(1, value);
5782480093f4SDimitry Andric       if (changd_attr)
5783480093f4SDimitry Andric         window.AttributeOff(changd_attr);
5784480093f4SDimitry Andric     }
5785480093f4SDimitry Andric 
5786480093f4SDimitry Andric     if (summary && summary[0]) {
5787e8d8bef9SDimitry Andric       window.PutCStringTruncated(1, " ");
5788480093f4SDimitry Andric       if (changd_attr)
5789480093f4SDimitry Andric         window.AttributeOn(changd_attr);
5790e8d8bef9SDimitry Andric       window.PutCStringTruncated(1, summary);
5791480093f4SDimitry Andric       if (changd_attr)
5792480093f4SDimitry Andric         window.AttributeOff(changd_attr);
5793480093f4SDimitry Andric     }
5794480093f4SDimitry Andric 
5795480093f4SDimitry Andric     if (highlight)
5796480093f4SDimitry Andric       window.AttributeOff(A_REVERSE);
5797480093f4SDimitry Andric 
5798480093f4SDimitry Andric     return true;
5799480093f4SDimitry Andric   }
5800480093f4SDimitry Andric 
5801480093f4SDimitry Andric   void DisplayRows(Window &window, std::vector<Row> &rows,
5802480093f4SDimitry Andric                    DisplayOptions &options) {
5803480093f4SDimitry Andric     // >   0x25B7
5804480093f4SDimitry Andric     // \/  0x25BD
5805480093f4SDimitry Andric 
5806480093f4SDimitry Andric     bool window_is_active = window.IsActive();
5807480093f4SDimitry Andric     for (auto &row : rows) {
5808480093f4SDimitry Andric       const bool last_child = row.parent && &rows[rows.size() - 1] == &row;
5809480093f4SDimitry Andric       // Save the row index in each Row structure
5810480093f4SDimitry Andric       row.row_idx = m_num_rows;
5811480093f4SDimitry Andric       if ((m_num_rows >= m_first_visible_row) &&
5812480093f4SDimitry Andric           ((m_num_rows - m_first_visible_row) <
5813480093f4SDimitry Andric            static_cast<size_t>(NumVisibleRows()))) {
5814480093f4SDimitry Andric         row.x = m_min_x;
5815480093f4SDimitry Andric         row.y = m_num_rows - m_first_visible_row + 1;
5816480093f4SDimitry Andric         if (DisplayRowObject(window, row, options,
5817480093f4SDimitry Andric                              window_is_active &&
5818480093f4SDimitry Andric                                  m_num_rows == m_selected_row_idx,
5819480093f4SDimitry Andric                              last_child)) {
5820480093f4SDimitry Andric           ++m_num_rows;
5821480093f4SDimitry Andric         } else {
5822480093f4SDimitry Andric           row.x = 0;
5823480093f4SDimitry Andric           row.y = 0;
5824480093f4SDimitry Andric         }
5825480093f4SDimitry Andric       } else {
5826480093f4SDimitry Andric         row.x = 0;
5827480093f4SDimitry Andric         row.y = 0;
5828480093f4SDimitry Andric         ++m_num_rows;
5829480093f4SDimitry Andric       }
5830480093f4SDimitry Andric 
583181ad6265SDimitry Andric       if (row.expanded) {
5832480093f4SDimitry Andric         auto &children = row.GetChildren();
583381ad6265SDimitry Andric         if (!children.empty()) {
5834480093f4SDimitry Andric           DisplayRows(window, children, options);
5835480093f4SDimitry Andric         }
5836480093f4SDimitry Andric       }
5837480093f4SDimitry Andric     }
583881ad6265SDimitry Andric   }
5839480093f4SDimitry Andric 
5840480093f4SDimitry Andric   int CalculateTotalNumberRows(std::vector<Row> &rows) {
5841480093f4SDimitry Andric     int row_count = 0;
5842480093f4SDimitry Andric     for (auto &row : rows) {
5843480093f4SDimitry Andric       ++row_count;
5844480093f4SDimitry Andric       if (row.expanded)
5845480093f4SDimitry Andric         row_count += CalculateTotalNumberRows(row.GetChildren());
5846480093f4SDimitry Andric     }
5847480093f4SDimitry Andric     return row_count;
5848480093f4SDimitry Andric   }
5849480093f4SDimitry Andric 
5850480093f4SDimitry Andric   static Row *GetRowForRowIndexImpl(std::vector<Row> &rows, size_t &row_index) {
5851480093f4SDimitry Andric     for (auto &row : rows) {
5852480093f4SDimitry Andric       if (row_index == 0)
5853480093f4SDimitry Andric         return &row;
5854480093f4SDimitry Andric       else {
5855480093f4SDimitry Andric         --row_index;
585681ad6265SDimitry Andric         if (row.expanded) {
5857480093f4SDimitry Andric           auto &children = row.GetChildren();
585881ad6265SDimitry Andric           if (!children.empty()) {
5859480093f4SDimitry Andric             Row *result = GetRowForRowIndexImpl(children, row_index);
5860480093f4SDimitry Andric             if (result)
5861480093f4SDimitry Andric               return result;
5862480093f4SDimitry Andric           }
5863480093f4SDimitry Andric         }
5864480093f4SDimitry Andric       }
586581ad6265SDimitry Andric     }
5866480093f4SDimitry Andric     return nullptr;
5867480093f4SDimitry Andric   }
5868480093f4SDimitry Andric 
5869480093f4SDimitry Andric   Row *GetRowForRowIndex(size_t row_index) {
5870480093f4SDimitry Andric     return GetRowForRowIndexImpl(m_rows, row_index);
5871480093f4SDimitry Andric   }
5872480093f4SDimitry Andric 
5873480093f4SDimitry Andric   int NumVisibleRows() const { return m_max_y - m_min_y; }
5874480093f4SDimitry Andric 
5875480093f4SDimitry Andric   static DisplayOptions g_options;
5876480093f4SDimitry Andric };
5877480093f4SDimitry Andric 
5878480093f4SDimitry Andric class FrameVariablesWindowDelegate : public ValueObjectListDelegate {
5879480093f4SDimitry Andric public:
5880480093f4SDimitry Andric   FrameVariablesWindowDelegate(Debugger &debugger)
588181ad6265SDimitry Andric       : ValueObjectListDelegate(), m_debugger(debugger) {}
5882480093f4SDimitry Andric 
5883480093f4SDimitry Andric   ~FrameVariablesWindowDelegate() override = default;
5884480093f4SDimitry Andric 
5885480093f4SDimitry Andric   const char *WindowDelegateGetHelpText() override {
5886480093f4SDimitry Andric     return "Frame variable window keyboard shortcuts:";
5887480093f4SDimitry Andric   }
5888480093f4SDimitry Andric 
5889480093f4SDimitry Andric   bool WindowDelegateDraw(Window &window, bool force) override {
5890480093f4SDimitry Andric     ExecutionContext exe_ctx(
5891480093f4SDimitry Andric         m_debugger.GetCommandInterpreter().GetExecutionContext());
5892480093f4SDimitry Andric     Process *process = exe_ctx.GetProcessPtr();
5893480093f4SDimitry Andric     Block *frame_block = nullptr;
5894480093f4SDimitry Andric     StackFrame *frame = nullptr;
5895480093f4SDimitry Andric 
5896480093f4SDimitry Andric     if (process) {
5897480093f4SDimitry Andric       StateType state = process->GetState();
5898480093f4SDimitry Andric       if (StateIsStoppedState(state, true)) {
5899480093f4SDimitry Andric         frame = exe_ctx.GetFramePtr();
5900480093f4SDimitry Andric         if (frame)
5901480093f4SDimitry Andric           frame_block = frame->GetFrameBlock();
5902480093f4SDimitry Andric       } else if (StateIsRunningState(state)) {
5903480093f4SDimitry Andric         return true; // Don't do any updating when we are running
5904480093f4SDimitry Andric       }
5905480093f4SDimitry Andric     }
5906480093f4SDimitry Andric 
5907480093f4SDimitry Andric     ValueObjectList local_values;
5908480093f4SDimitry Andric     if (frame_block) {
5909480093f4SDimitry Andric       // Only update the variables if they have changed
5910480093f4SDimitry Andric       if (m_frame_block != frame_block) {
5911480093f4SDimitry Andric         m_frame_block = frame_block;
5912480093f4SDimitry Andric 
5913bdd1243dSDimitry Andric         VariableList *locals = frame->GetVariableList(true, nullptr);
5914480093f4SDimitry Andric         if (locals) {
5915480093f4SDimitry Andric           const DynamicValueType use_dynamic = eDynamicDontRunTarget;
5916480093f4SDimitry Andric           for (const VariableSP &local_sp : *locals) {
5917480093f4SDimitry Andric             ValueObjectSP value_sp =
5918480093f4SDimitry Andric                 frame->GetValueObjectForFrameVariable(local_sp, use_dynamic);
5919480093f4SDimitry Andric             if (value_sp) {
5920480093f4SDimitry Andric               ValueObjectSP synthetic_value_sp = value_sp->GetSyntheticValue();
5921480093f4SDimitry Andric               if (synthetic_value_sp)
5922480093f4SDimitry Andric                 local_values.Append(synthetic_value_sp);
5923480093f4SDimitry Andric               else
5924480093f4SDimitry Andric                 local_values.Append(value_sp);
5925480093f4SDimitry Andric             }
5926480093f4SDimitry Andric           }
5927480093f4SDimitry Andric           // Update the values
5928480093f4SDimitry Andric           SetValues(local_values);
5929480093f4SDimitry Andric         }
5930480093f4SDimitry Andric       }
5931480093f4SDimitry Andric     } else {
5932480093f4SDimitry Andric       m_frame_block = nullptr;
5933480093f4SDimitry Andric       // Update the values with an empty list if there is no frame
5934480093f4SDimitry Andric       SetValues(local_values);
5935480093f4SDimitry Andric     }
5936480093f4SDimitry Andric 
5937480093f4SDimitry Andric     return ValueObjectListDelegate::WindowDelegateDraw(window, force);
5938480093f4SDimitry Andric   }
5939480093f4SDimitry Andric 
5940480093f4SDimitry Andric protected:
5941480093f4SDimitry Andric   Debugger &m_debugger;
594281ad6265SDimitry Andric   Block *m_frame_block = nullptr;
5943480093f4SDimitry Andric };
5944480093f4SDimitry Andric 
5945480093f4SDimitry Andric class RegistersWindowDelegate : public ValueObjectListDelegate {
5946480093f4SDimitry Andric public:
5947480093f4SDimitry Andric   RegistersWindowDelegate(Debugger &debugger)
5948480093f4SDimitry Andric       : ValueObjectListDelegate(), m_debugger(debugger) {}
5949480093f4SDimitry Andric 
5950480093f4SDimitry Andric   ~RegistersWindowDelegate() override = default;
5951480093f4SDimitry Andric 
5952480093f4SDimitry Andric   const char *WindowDelegateGetHelpText() override {
5953480093f4SDimitry Andric     return "Register window keyboard shortcuts:";
5954480093f4SDimitry Andric   }
5955480093f4SDimitry Andric 
5956480093f4SDimitry Andric   bool WindowDelegateDraw(Window &window, bool force) override {
5957480093f4SDimitry Andric     ExecutionContext exe_ctx(
5958480093f4SDimitry Andric         m_debugger.GetCommandInterpreter().GetExecutionContext());
5959480093f4SDimitry Andric     StackFrame *frame = exe_ctx.GetFramePtr();
5960480093f4SDimitry Andric 
5961480093f4SDimitry Andric     ValueObjectList value_list;
5962480093f4SDimitry Andric     if (frame) {
5963480093f4SDimitry Andric       if (frame->GetStackID() != m_stack_id) {
5964480093f4SDimitry Andric         m_stack_id = frame->GetStackID();
5965480093f4SDimitry Andric         RegisterContextSP reg_ctx(frame->GetRegisterContext());
5966480093f4SDimitry Andric         if (reg_ctx) {
5967480093f4SDimitry Andric           const uint32_t num_sets = reg_ctx->GetRegisterSetCount();
5968480093f4SDimitry Andric           for (uint32_t set_idx = 0; set_idx < num_sets; ++set_idx) {
5969480093f4SDimitry Andric             value_list.Append(
5970480093f4SDimitry Andric                 ValueObjectRegisterSet::Create(frame, reg_ctx, set_idx));
5971480093f4SDimitry Andric           }
5972480093f4SDimitry Andric         }
5973480093f4SDimitry Andric         SetValues(value_list);
5974480093f4SDimitry Andric       }
5975480093f4SDimitry Andric     } else {
5976480093f4SDimitry Andric       Process *process = exe_ctx.GetProcessPtr();
5977480093f4SDimitry Andric       if (process && process->IsAlive())
5978480093f4SDimitry Andric         return true; // Don't do any updating if we are running
5979480093f4SDimitry Andric       else {
5980480093f4SDimitry Andric         // Update the values with an empty list if there is no process or the
5981480093f4SDimitry Andric         // process isn't alive anymore
5982480093f4SDimitry Andric         SetValues(value_list);
5983480093f4SDimitry Andric       }
5984480093f4SDimitry Andric     }
5985480093f4SDimitry Andric     return ValueObjectListDelegate::WindowDelegateDraw(window, force);
5986480093f4SDimitry Andric   }
5987480093f4SDimitry Andric 
5988480093f4SDimitry Andric protected:
5989480093f4SDimitry Andric   Debugger &m_debugger;
5990480093f4SDimitry Andric   StackID m_stack_id;
5991480093f4SDimitry Andric };
5992480093f4SDimitry Andric 
5993480093f4SDimitry Andric static const char *CursesKeyToCString(int ch) {
5994480093f4SDimitry Andric   static char g_desc[32];
5995480093f4SDimitry Andric   if (ch >= KEY_F0 && ch < KEY_F0 + 64) {
5996480093f4SDimitry Andric     snprintf(g_desc, sizeof(g_desc), "F%u", ch - KEY_F0);
5997480093f4SDimitry Andric     return g_desc;
5998480093f4SDimitry Andric   }
5999480093f4SDimitry Andric   switch (ch) {
6000480093f4SDimitry Andric   case KEY_DOWN:
6001480093f4SDimitry Andric     return "down";
6002480093f4SDimitry Andric   case KEY_UP:
6003480093f4SDimitry Andric     return "up";
6004480093f4SDimitry Andric   case KEY_LEFT:
6005480093f4SDimitry Andric     return "left";
6006480093f4SDimitry Andric   case KEY_RIGHT:
6007480093f4SDimitry Andric     return "right";
6008480093f4SDimitry Andric   case KEY_HOME:
6009480093f4SDimitry Andric     return "home";
6010480093f4SDimitry Andric   case KEY_BACKSPACE:
6011480093f4SDimitry Andric     return "backspace";
6012480093f4SDimitry Andric   case KEY_DL:
6013480093f4SDimitry Andric     return "delete-line";
6014480093f4SDimitry Andric   case KEY_IL:
6015480093f4SDimitry Andric     return "insert-line";
6016480093f4SDimitry Andric   case KEY_DC:
6017480093f4SDimitry Andric     return "delete-char";
6018480093f4SDimitry Andric   case KEY_IC:
6019480093f4SDimitry Andric     return "insert-char";
6020480093f4SDimitry Andric   case KEY_CLEAR:
6021480093f4SDimitry Andric     return "clear";
6022480093f4SDimitry Andric   case KEY_EOS:
6023480093f4SDimitry Andric     return "clear-to-eos";
6024480093f4SDimitry Andric   case KEY_EOL:
6025480093f4SDimitry Andric     return "clear-to-eol";
6026480093f4SDimitry Andric   case KEY_SF:
6027480093f4SDimitry Andric     return "scroll-forward";
6028480093f4SDimitry Andric   case KEY_SR:
6029480093f4SDimitry Andric     return "scroll-backward";
6030480093f4SDimitry Andric   case KEY_NPAGE:
6031480093f4SDimitry Andric     return "page-down";
6032480093f4SDimitry Andric   case KEY_PPAGE:
6033480093f4SDimitry Andric     return "page-up";
6034480093f4SDimitry Andric   case KEY_STAB:
6035480093f4SDimitry Andric     return "set-tab";
6036480093f4SDimitry Andric   case KEY_CTAB:
6037480093f4SDimitry Andric     return "clear-tab";
6038480093f4SDimitry Andric   case KEY_CATAB:
6039480093f4SDimitry Andric     return "clear-all-tabs";
6040480093f4SDimitry Andric   case KEY_ENTER:
6041480093f4SDimitry Andric     return "enter";
6042480093f4SDimitry Andric   case KEY_PRINT:
6043480093f4SDimitry Andric     return "print";
6044480093f4SDimitry Andric   case KEY_LL:
6045480093f4SDimitry Andric     return "lower-left key";
6046480093f4SDimitry Andric   case KEY_A1:
6047480093f4SDimitry Andric     return "upper left of keypad";
6048480093f4SDimitry Andric   case KEY_A3:
6049480093f4SDimitry Andric     return "upper right of keypad";
6050480093f4SDimitry Andric   case KEY_B2:
6051480093f4SDimitry Andric     return "center of keypad";
6052480093f4SDimitry Andric   case KEY_C1:
6053480093f4SDimitry Andric     return "lower left of keypad";
6054480093f4SDimitry Andric   case KEY_C3:
6055480093f4SDimitry Andric     return "lower right of keypad";
6056480093f4SDimitry Andric   case KEY_BTAB:
6057480093f4SDimitry Andric     return "back-tab key";
6058480093f4SDimitry Andric   case KEY_BEG:
6059480093f4SDimitry Andric     return "begin key";
6060480093f4SDimitry Andric   case KEY_CANCEL:
6061480093f4SDimitry Andric     return "cancel key";
6062480093f4SDimitry Andric   case KEY_CLOSE:
6063480093f4SDimitry Andric     return "close key";
6064480093f4SDimitry Andric   case KEY_COMMAND:
6065480093f4SDimitry Andric     return "command key";
6066480093f4SDimitry Andric   case KEY_COPY:
6067480093f4SDimitry Andric     return "copy key";
6068480093f4SDimitry Andric   case KEY_CREATE:
6069480093f4SDimitry Andric     return "create key";
6070480093f4SDimitry Andric   case KEY_END:
6071480093f4SDimitry Andric     return "end key";
6072480093f4SDimitry Andric   case KEY_EXIT:
6073480093f4SDimitry Andric     return "exit key";
6074480093f4SDimitry Andric   case KEY_FIND:
6075480093f4SDimitry Andric     return "find key";
6076480093f4SDimitry Andric   case KEY_HELP:
6077480093f4SDimitry Andric     return "help key";
6078480093f4SDimitry Andric   case KEY_MARK:
6079480093f4SDimitry Andric     return "mark key";
6080480093f4SDimitry Andric   case KEY_MESSAGE:
6081480093f4SDimitry Andric     return "message key";
6082480093f4SDimitry Andric   case KEY_MOVE:
6083480093f4SDimitry Andric     return "move key";
6084480093f4SDimitry Andric   case KEY_NEXT:
6085480093f4SDimitry Andric     return "next key";
6086480093f4SDimitry Andric   case KEY_OPEN:
6087480093f4SDimitry Andric     return "open key";
6088480093f4SDimitry Andric   case KEY_OPTIONS:
6089480093f4SDimitry Andric     return "options key";
6090480093f4SDimitry Andric   case KEY_PREVIOUS:
6091480093f4SDimitry Andric     return "previous key";
6092480093f4SDimitry Andric   case KEY_REDO:
6093480093f4SDimitry Andric     return "redo key";
6094480093f4SDimitry Andric   case KEY_REFERENCE:
6095480093f4SDimitry Andric     return "reference key";
6096480093f4SDimitry Andric   case KEY_REFRESH:
6097480093f4SDimitry Andric     return "refresh key";
6098480093f4SDimitry Andric   case KEY_REPLACE:
6099480093f4SDimitry Andric     return "replace key";
6100480093f4SDimitry Andric   case KEY_RESTART:
6101480093f4SDimitry Andric     return "restart key";
6102480093f4SDimitry Andric   case KEY_RESUME:
6103480093f4SDimitry Andric     return "resume key";
6104480093f4SDimitry Andric   case KEY_SAVE:
6105480093f4SDimitry Andric     return "save key";
6106480093f4SDimitry Andric   case KEY_SBEG:
6107480093f4SDimitry Andric     return "shifted begin key";
6108480093f4SDimitry Andric   case KEY_SCANCEL:
6109480093f4SDimitry Andric     return "shifted cancel key";
6110480093f4SDimitry Andric   case KEY_SCOMMAND:
6111480093f4SDimitry Andric     return "shifted command key";
6112480093f4SDimitry Andric   case KEY_SCOPY:
6113480093f4SDimitry Andric     return "shifted copy key";
6114480093f4SDimitry Andric   case KEY_SCREATE:
6115480093f4SDimitry Andric     return "shifted create key";
6116480093f4SDimitry Andric   case KEY_SDC:
6117480093f4SDimitry Andric     return "shifted delete-character key";
6118480093f4SDimitry Andric   case KEY_SDL:
6119480093f4SDimitry Andric     return "shifted delete-line key";
6120480093f4SDimitry Andric   case KEY_SELECT:
6121480093f4SDimitry Andric     return "select key";
6122480093f4SDimitry Andric   case KEY_SEND:
6123480093f4SDimitry Andric     return "shifted end key";
6124480093f4SDimitry Andric   case KEY_SEOL:
6125480093f4SDimitry Andric     return "shifted clear-to-end-of-line key";
6126480093f4SDimitry Andric   case KEY_SEXIT:
6127480093f4SDimitry Andric     return "shifted exit key";
6128480093f4SDimitry Andric   case KEY_SFIND:
6129480093f4SDimitry Andric     return "shifted find key";
6130480093f4SDimitry Andric   case KEY_SHELP:
6131480093f4SDimitry Andric     return "shifted help key";
6132480093f4SDimitry Andric   case KEY_SHOME:
6133480093f4SDimitry Andric     return "shifted home key";
6134480093f4SDimitry Andric   case KEY_SIC:
6135480093f4SDimitry Andric     return "shifted insert-character key";
6136480093f4SDimitry Andric   case KEY_SLEFT:
6137480093f4SDimitry Andric     return "shifted left-arrow key";
6138480093f4SDimitry Andric   case KEY_SMESSAGE:
6139480093f4SDimitry Andric     return "shifted message key";
6140480093f4SDimitry Andric   case KEY_SMOVE:
6141480093f4SDimitry Andric     return "shifted move key";
6142480093f4SDimitry Andric   case KEY_SNEXT:
6143480093f4SDimitry Andric     return "shifted next key";
6144480093f4SDimitry Andric   case KEY_SOPTIONS:
6145480093f4SDimitry Andric     return "shifted options key";
6146480093f4SDimitry Andric   case KEY_SPREVIOUS:
6147480093f4SDimitry Andric     return "shifted previous key";
6148480093f4SDimitry Andric   case KEY_SPRINT:
6149480093f4SDimitry Andric     return "shifted print key";
6150480093f4SDimitry Andric   case KEY_SREDO:
6151480093f4SDimitry Andric     return "shifted redo key";
6152480093f4SDimitry Andric   case KEY_SREPLACE:
6153480093f4SDimitry Andric     return "shifted replace key";
6154480093f4SDimitry Andric   case KEY_SRIGHT:
6155480093f4SDimitry Andric     return "shifted right-arrow key";
6156480093f4SDimitry Andric   case KEY_SRSUME:
6157480093f4SDimitry Andric     return "shifted resume key";
6158480093f4SDimitry Andric   case KEY_SSAVE:
6159480093f4SDimitry Andric     return "shifted save key";
6160480093f4SDimitry Andric   case KEY_SSUSPEND:
6161480093f4SDimitry Andric     return "shifted suspend key";
6162480093f4SDimitry Andric   case KEY_SUNDO:
6163480093f4SDimitry Andric     return "shifted undo key";
6164480093f4SDimitry Andric   case KEY_SUSPEND:
6165480093f4SDimitry Andric     return "suspend key";
6166480093f4SDimitry Andric   case KEY_UNDO:
6167480093f4SDimitry Andric     return "undo key";
6168480093f4SDimitry Andric   case KEY_MOUSE:
6169480093f4SDimitry Andric     return "Mouse event has occurred";
6170480093f4SDimitry Andric   case KEY_RESIZE:
6171480093f4SDimitry Andric     return "Terminal resize event";
6172480093f4SDimitry Andric #ifdef KEY_EVENT
6173480093f4SDimitry Andric   case KEY_EVENT:
6174480093f4SDimitry Andric     return "We were interrupted by an event";
6175480093f4SDimitry Andric #endif
6176480093f4SDimitry Andric   case KEY_RETURN:
6177480093f4SDimitry Andric     return "return";
6178480093f4SDimitry Andric   case ' ':
6179480093f4SDimitry Andric     return "space";
6180480093f4SDimitry Andric   case '\t':
6181480093f4SDimitry Andric     return "tab";
6182480093f4SDimitry Andric   case KEY_ESCAPE:
6183480093f4SDimitry Andric     return "escape";
6184480093f4SDimitry Andric   default:
61855ffd83dbSDimitry Andric     if (llvm::isPrint(ch))
6186480093f4SDimitry Andric       snprintf(g_desc, sizeof(g_desc), "%c", ch);
6187480093f4SDimitry Andric     else
6188480093f4SDimitry Andric       snprintf(g_desc, sizeof(g_desc), "\\x%2.2x", ch);
6189480093f4SDimitry Andric     return g_desc;
6190480093f4SDimitry Andric   }
6191480093f4SDimitry Andric   return nullptr;
6192480093f4SDimitry Andric }
6193480093f4SDimitry Andric 
6194480093f4SDimitry Andric HelpDialogDelegate::HelpDialogDelegate(const char *text,
6195480093f4SDimitry Andric                                        KeyHelp *key_help_array)
619681ad6265SDimitry Andric     : m_text() {
6197480093f4SDimitry Andric   if (text && text[0]) {
6198480093f4SDimitry Andric     m_text.SplitIntoLines(text);
6199480093f4SDimitry Andric     m_text.AppendString("");
6200480093f4SDimitry Andric   }
6201480093f4SDimitry Andric   if (key_help_array) {
6202480093f4SDimitry Andric     for (KeyHelp *key = key_help_array; key->ch; ++key) {
6203480093f4SDimitry Andric       StreamString key_description;
6204480093f4SDimitry Andric       key_description.Printf("%10s - %s", CursesKeyToCString(key->ch),
6205480093f4SDimitry Andric                              key->description);
6206480093f4SDimitry Andric       m_text.AppendString(key_description.GetString());
6207480093f4SDimitry Andric     }
6208480093f4SDimitry Andric   }
6209480093f4SDimitry Andric }
6210480093f4SDimitry Andric 
6211480093f4SDimitry Andric HelpDialogDelegate::~HelpDialogDelegate() = default;
6212480093f4SDimitry Andric 
6213480093f4SDimitry Andric bool HelpDialogDelegate::WindowDelegateDraw(Window &window, bool force) {
6214480093f4SDimitry Andric   window.Erase();
6215480093f4SDimitry Andric   const int window_height = window.GetHeight();
6216480093f4SDimitry Andric   int x = 2;
6217480093f4SDimitry Andric   int y = 1;
6218480093f4SDimitry Andric   const int min_y = y;
6219480093f4SDimitry Andric   const int max_y = window_height - 1 - y;
6220480093f4SDimitry Andric   const size_t num_visible_lines = max_y - min_y + 1;
6221480093f4SDimitry Andric   const size_t num_lines = m_text.GetSize();
6222480093f4SDimitry Andric   const char *bottom_message;
6223480093f4SDimitry Andric   if (num_lines <= num_visible_lines)
6224480093f4SDimitry Andric     bottom_message = "Press any key to exit";
6225480093f4SDimitry Andric   else
6226480093f4SDimitry Andric     bottom_message = "Use arrows to scroll, any other key to exit";
6227480093f4SDimitry Andric   window.DrawTitleBox(window.GetName(), bottom_message);
6228480093f4SDimitry Andric   while (y <= max_y) {
6229480093f4SDimitry Andric     window.MoveCursor(x, y);
6230480093f4SDimitry Andric     window.PutCStringTruncated(
6231e8d8bef9SDimitry Andric         1, m_text.GetStringAtIndex(m_first_visible_line + y - min_y));
6232480093f4SDimitry Andric     ++y;
6233480093f4SDimitry Andric   }
6234480093f4SDimitry Andric   return true;
6235480093f4SDimitry Andric }
6236480093f4SDimitry Andric 
6237480093f4SDimitry Andric HandleCharResult HelpDialogDelegate::WindowDelegateHandleChar(Window &window,
6238480093f4SDimitry Andric                                                               int key) {
6239480093f4SDimitry Andric   bool done = false;
6240480093f4SDimitry Andric   const size_t num_lines = m_text.GetSize();
6241480093f4SDimitry Andric   const size_t num_visible_lines = window.GetHeight() - 2;
6242480093f4SDimitry Andric 
6243480093f4SDimitry Andric   if (num_lines <= num_visible_lines) {
6244480093f4SDimitry Andric     done = true;
6245480093f4SDimitry Andric     // If we have all lines visible and don't need scrolling, then any key
6246480093f4SDimitry Andric     // press will cause us to exit
6247480093f4SDimitry Andric   } else {
6248480093f4SDimitry Andric     switch (key) {
6249480093f4SDimitry Andric     case KEY_UP:
6250480093f4SDimitry Andric       if (m_first_visible_line > 0)
6251480093f4SDimitry Andric         --m_first_visible_line;
6252480093f4SDimitry Andric       break;
6253480093f4SDimitry Andric 
6254480093f4SDimitry Andric     case KEY_DOWN:
6255480093f4SDimitry Andric       if (m_first_visible_line + num_visible_lines < num_lines)
6256480093f4SDimitry Andric         ++m_first_visible_line;
6257480093f4SDimitry Andric       break;
6258480093f4SDimitry Andric 
6259480093f4SDimitry Andric     case KEY_PPAGE:
6260480093f4SDimitry Andric     case ',':
6261480093f4SDimitry Andric       if (m_first_visible_line > 0) {
6262480093f4SDimitry Andric         if (static_cast<size_t>(m_first_visible_line) >= num_visible_lines)
6263480093f4SDimitry Andric           m_first_visible_line -= num_visible_lines;
6264480093f4SDimitry Andric         else
6265480093f4SDimitry Andric           m_first_visible_line = 0;
6266480093f4SDimitry Andric       }
6267480093f4SDimitry Andric       break;
6268480093f4SDimitry Andric 
6269480093f4SDimitry Andric     case KEY_NPAGE:
6270480093f4SDimitry Andric     case '.':
6271480093f4SDimitry Andric       if (m_first_visible_line + num_visible_lines < num_lines) {
6272480093f4SDimitry Andric         m_first_visible_line += num_visible_lines;
6273480093f4SDimitry Andric         if (static_cast<size_t>(m_first_visible_line) > num_lines)
6274480093f4SDimitry Andric           m_first_visible_line = num_lines - num_visible_lines;
6275480093f4SDimitry Andric       }
6276480093f4SDimitry Andric       break;
6277480093f4SDimitry Andric 
6278480093f4SDimitry Andric     default:
6279480093f4SDimitry Andric       done = true;
6280480093f4SDimitry Andric       break;
6281480093f4SDimitry Andric     }
6282480093f4SDimitry Andric   }
6283480093f4SDimitry Andric   if (done)
6284480093f4SDimitry Andric     window.GetParent()->RemoveSubWindow(&window);
6285480093f4SDimitry Andric   return eKeyHandled;
6286480093f4SDimitry Andric }
6287480093f4SDimitry Andric 
6288480093f4SDimitry Andric class ApplicationDelegate : public WindowDelegate, public MenuDelegate {
6289480093f4SDimitry Andric public:
6290480093f4SDimitry Andric   enum {
6291480093f4SDimitry Andric     eMenuID_LLDB = 1,
6292480093f4SDimitry Andric     eMenuID_LLDBAbout,
6293480093f4SDimitry Andric     eMenuID_LLDBExit,
6294480093f4SDimitry Andric 
6295480093f4SDimitry Andric     eMenuID_Target,
6296480093f4SDimitry Andric     eMenuID_TargetCreate,
6297480093f4SDimitry Andric     eMenuID_TargetDelete,
6298480093f4SDimitry Andric 
6299480093f4SDimitry Andric     eMenuID_Process,
6300480093f4SDimitry Andric     eMenuID_ProcessAttach,
6301e8d8bef9SDimitry Andric     eMenuID_ProcessDetachResume,
6302e8d8bef9SDimitry Andric     eMenuID_ProcessDetachSuspended,
6303480093f4SDimitry Andric     eMenuID_ProcessLaunch,
6304480093f4SDimitry Andric     eMenuID_ProcessContinue,
6305480093f4SDimitry Andric     eMenuID_ProcessHalt,
6306480093f4SDimitry Andric     eMenuID_ProcessKill,
6307480093f4SDimitry Andric 
6308480093f4SDimitry Andric     eMenuID_Thread,
6309480093f4SDimitry Andric     eMenuID_ThreadStepIn,
6310480093f4SDimitry Andric     eMenuID_ThreadStepOver,
6311480093f4SDimitry Andric     eMenuID_ThreadStepOut,
6312480093f4SDimitry Andric 
6313480093f4SDimitry Andric     eMenuID_View,
6314480093f4SDimitry Andric     eMenuID_ViewBacktrace,
6315480093f4SDimitry Andric     eMenuID_ViewRegisters,
6316480093f4SDimitry Andric     eMenuID_ViewSource,
6317480093f4SDimitry Andric     eMenuID_ViewVariables,
6318349cc55cSDimitry Andric     eMenuID_ViewBreakpoints,
6319480093f4SDimitry Andric 
6320480093f4SDimitry Andric     eMenuID_Help,
6321480093f4SDimitry Andric     eMenuID_HelpGUIHelp
6322480093f4SDimitry Andric   };
6323480093f4SDimitry Andric 
6324480093f4SDimitry Andric   ApplicationDelegate(Application &app, Debugger &debugger)
6325480093f4SDimitry Andric       : WindowDelegate(), MenuDelegate(), m_app(app), m_debugger(debugger) {}
6326480093f4SDimitry Andric 
6327480093f4SDimitry Andric   ~ApplicationDelegate() override = default;
6328480093f4SDimitry Andric 
6329480093f4SDimitry Andric   bool WindowDelegateDraw(Window &window, bool force) override {
6330480093f4SDimitry Andric     return false; // Drawing not handled, let standard window drawing happen
6331480093f4SDimitry Andric   }
6332480093f4SDimitry Andric 
6333480093f4SDimitry Andric   HandleCharResult WindowDelegateHandleChar(Window &window, int key) override {
6334480093f4SDimitry Andric     switch (key) {
6335480093f4SDimitry Andric     case '\t':
6336480093f4SDimitry Andric       window.SelectNextWindowAsActive();
6337480093f4SDimitry Andric       return eKeyHandled;
6338480093f4SDimitry Andric 
6339fe6060f1SDimitry Andric     case KEY_SHIFT_TAB:
6340e8d8bef9SDimitry Andric       window.SelectPreviousWindowAsActive();
6341e8d8bef9SDimitry Andric       return eKeyHandled;
6342e8d8bef9SDimitry Andric 
6343480093f4SDimitry Andric     case 'h':
6344480093f4SDimitry Andric       window.CreateHelpSubwindow();
6345480093f4SDimitry Andric       return eKeyHandled;
6346480093f4SDimitry Andric 
6347480093f4SDimitry Andric     case KEY_ESCAPE:
6348480093f4SDimitry Andric       return eQuitApplication;
6349480093f4SDimitry Andric 
6350480093f4SDimitry Andric     default:
6351480093f4SDimitry Andric       break;
6352480093f4SDimitry Andric     }
6353480093f4SDimitry Andric     return eKeyNotHandled;
6354480093f4SDimitry Andric   }
6355480093f4SDimitry Andric 
6356480093f4SDimitry Andric   const char *WindowDelegateGetHelpText() override {
6357480093f4SDimitry Andric     return "Welcome to the LLDB curses GUI.\n\n"
6358480093f4SDimitry Andric            "Press the TAB key to change the selected view.\n"
6359480093f4SDimitry Andric            "Each view has its own keyboard shortcuts, press 'h' to open a "
6360480093f4SDimitry Andric            "dialog to display them.\n\n"
6361480093f4SDimitry Andric            "Common key bindings for all views:";
6362480093f4SDimitry Andric   }
6363480093f4SDimitry Andric 
6364480093f4SDimitry Andric   KeyHelp *WindowDelegateGetKeyHelp() override {
6365480093f4SDimitry Andric     static curses::KeyHelp g_source_view_key_help[] = {
6366480093f4SDimitry Andric         {'\t', "Select next view"},
6367e8d8bef9SDimitry Andric         {KEY_BTAB, "Select previous view"},
6368480093f4SDimitry Andric         {'h', "Show help dialog with view specific key bindings"},
6369480093f4SDimitry Andric         {',', "Page up"},
6370480093f4SDimitry Andric         {'.', "Page down"},
6371480093f4SDimitry Andric         {KEY_UP, "Select previous"},
6372480093f4SDimitry Andric         {KEY_DOWN, "Select next"},
6373480093f4SDimitry Andric         {KEY_LEFT, "Unexpand or select parent"},
6374480093f4SDimitry Andric         {KEY_RIGHT, "Expand"},
6375480093f4SDimitry Andric         {KEY_PPAGE, "Page up"},
6376480093f4SDimitry Andric         {KEY_NPAGE, "Page down"},
6377480093f4SDimitry Andric         {'\0', nullptr}};
6378480093f4SDimitry Andric     return g_source_view_key_help;
6379480093f4SDimitry Andric   }
6380480093f4SDimitry Andric 
6381480093f4SDimitry Andric   MenuActionResult MenuDelegateAction(Menu &menu) override {
6382480093f4SDimitry Andric     switch (menu.GetIdentifier()) {
6383349cc55cSDimitry Andric     case eMenuID_TargetCreate: {
6384349cc55cSDimitry Andric       WindowSP main_window_sp = m_app.GetMainWindow();
6385349cc55cSDimitry Andric       FormDelegateSP form_delegate_sp =
6386349cc55cSDimitry Andric           FormDelegateSP(new TargetCreateFormDelegate(m_debugger));
6387349cc55cSDimitry Andric       Rect bounds = main_window_sp->GetCenteredRect(80, 19);
6388349cc55cSDimitry Andric       WindowSP form_window_sp = main_window_sp->CreateSubWindow(
6389349cc55cSDimitry Andric           form_delegate_sp->GetName().c_str(), bounds, true);
6390349cc55cSDimitry Andric       WindowDelegateSP window_delegate_sp =
6391349cc55cSDimitry Andric           WindowDelegateSP(new FormWindowDelegate(form_delegate_sp));
6392349cc55cSDimitry Andric       form_window_sp->SetDelegate(window_delegate_sp);
6393349cc55cSDimitry Andric       return MenuActionResult::Handled;
6394349cc55cSDimitry Andric     }
6395480093f4SDimitry Andric     case eMenuID_ThreadStepIn: {
6396480093f4SDimitry Andric       ExecutionContext exe_ctx =
6397480093f4SDimitry Andric           m_debugger.GetCommandInterpreter().GetExecutionContext();
6398480093f4SDimitry Andric       if (exe_ctx.HasThreadScope()) {
6399480093f4SDimitry Andric         Process *process = exe_ctx.GetProcessPtr();
6400480093f4SDimitry Andric         if (process && process->IsAlive() &&
6401480093f4SDimitry Andric             StateIsStoppedState(process->GetState(), true))
6402480093f4SDimitry Andric           exe_ctx.GetThreadRef().StepIn(true);
6403480093f4SDimitry Andric       }
6404480093f4SDimitry Andric     }
6405480093f4SDimitry Andric       return MenuActionResult::Handled;
6406480093f4SDimitry Andric 
6407480093f4SDimitry Andric     case eMenuID_ThreadStepOut: {
6408480093f4SDimitry Andric       ExecutionContext exe_ctx =
6409480093f4SDimitry Andric           m_debugger.GetCommandInterpreter().GetExecutionContext();
6410480093f4SDimitry Andric       if (exe_ctx.HasThreadScope()) {
6411480093f4SDimitry Andric         Process *process = exe_ctx.GetProcessPtr();
6412480093f4SDimitry Andric         if (process && process->IsAlive() &&
641381ad6265SDimitry Andric             StateIsStoppedState(process->GetState(), true)) {
641481ad6265SDimitry Andric           Thread *thread = exe_ctx.GetThreadPtr();
6415*06c3fb27SDimitry Andric           uint32_t frame_idx =
6416*06c3fb27SDimitry Andric               thread->GetSelectedFrameIndex(SelectMostRelevantFrame);
641781ad6265SDimitry Andric           exe_ctx.GetThreadRef().StepOut(frame_idx);
641881ad6265SDimitry Andric         }
6419480093f4SDimitry Andric       }
6420480093f4SDimitry Andric     }
6421480093f4SDimitry Andric       return MenuActionResult::Handled;
6422480093f4SDimitry Andric 
6423480093f4SDimitry Andric     case eMenuID_ThreadStepOver: {
6424480093f4SDimitry Andric       ExecutionContext exe_ctx =
6425480093f4SDimitry Andric           m_debugger.GetCommandInterpreter().GetExecutionContext();
6426480093f4SDimitry Andric       if (exe_ctx.HasThreadScope()) {
6427480093f4SDimitry Andric         Process *process = exe_ctx.GetProcessPtr();
6428480093f4SDimitry Andric         if (process && process->IsAlive() &&
6429480093f4SDimitry Andric             StateIsStoppedState(process->GetState(), true))
6430480093f4SDimitry Andric           exe_ctx.GetThreadRef().StepOver(true);
6431480093f4SDimitry Andric       }
6432480093f4SDimitry Andric     }
6433480093f4SDimitry Andric       return MenuActionResult::Handled;
6434480093f4SDimitry Andric 
6435fe6060f1SDimitry Andric     case eMenuID_ProcessAttach: {
6436fe6060f1SDimitry Andric       WindowSP main_window_sp = m_app.GetMainWindow();
6437fe6060f1SDimitry Andric       FormDelegateSP form_delegate_sp = FormDelegateSP(
6438fe6060f1SDimitry Andric           new ProcessAttachFormDelegate(m_debugger, main_window_sp));
6439fe6060f1SDimitry Andric       Rect bounds = main_window_sp->GetCenteredRect(80, 22);
6440fe6060f1SDimitry Andric       WindowSP form_window_sp = main_window_sp->CreateSubWindow(
6441fe6060f1SDimitry Andric           form_delegate_sp->GetName().c_str(), bounds, true);
6442fe6060f1SDimitry Andric       WindowDelegateSP window_delegate_sp =
6443fe6060f1SDimitry Andric           WindowDelegateSP(new FormWindowDelegate(form_delegate_sp));
6444fe6060f1SDimitry Andric       form_window_sp->SetDelegate(window_delegate_sp);
6445fe6060f1SDimitry Andric       return MenuActionResult::Handled;
6446fe6060f1SDimitry Andric     }
6447349cc55cSDimitry Andric     case eMenuID_ProcessLaunch: {
6448349cc55cSDimitry Andric       WindowSP main_window_sp = m_app.GetMainWindow();
6449349cc55cSDimitry Andric       FormDelegateSP form_delegate_sp = FormDelegateSP(
6450349cc55cSDimitry Andric           new ProcessLaunchFormDelegate(m_debugger, main_window_sp));
6451349cc55cSDimitry Andric       Rect bounds = main_window_sp->GetCenteredRect(80, 22);
6452349cc55cSDimitry Andric       WindowSP form_window_sp = main_window_sp->CreateSubWindow(
6453349cc55cSDimitry Andric           form_delegate_sp->GetName().c_str(), bounds, true);
6454349cc55cSDimitry Andric       WindowDelegateSP window_delegate_sp =
6455349cc55cSDimitry Andric           WindowDelegateSP(new FormWindowDelegate(form_delegate_sp));
6456349cc55cSDimitry Andric       form_window_sp->SetDelegate(window_delegate_sp);
6457349cc55cSDimitry Andric       return MenuActionResult::Handled;
6458349cc55cSDimitry Andric     }
6459fe6060f1SDimitry Andric 
6460480093f4SDimitry Andric     case eMenuID_ProcessContinue: {
6461480093f4SDimitry Andric       ExecutionContext exe_ctx =
6462480093f4SDimitry Andric           m_debugger.GetCommandInterpreter().GetExecutionContext();
6463480093f4SDimitry Andric       if (exe_ctx.HasProcessScope()) {
6464480093f4SDimitry Andric         Process *process = exe_ctx.GetProcessPtr();
6465480093f4SDimitry Andric         if (process && process->IsAlive() &&
6466480093f4SDimitry Andric             StateIsStoppedState(process->GetState(), true))
6467480093f4SDimitry Andric           process->Resume();
6468480093f4SDimitry Andric       }
6469480093f4SDimitry Andric     }
6470480093f4SDimitry Andric       return MenuActionResult::Handled;
6471480093f4SDimitry Andric 
6472480093f4SDimitry Andric     case eMenuID_ProcessKill: {
6473480093f4SDimitry Andric       ExecutionContext exe_ctx =
6474480093f4SDimitry Andric           m_debugger.GetCommandInterpreter().GetExecutionContext();
6475480093f4SDimitry Andric       if (exe_ctx.HasProcessScope()) {
6476480093f4SDimitry Andric         Process *process = exe_ctx.GetProcessPtr();
6477480093f4SDimitry Andric         if (process && process->IsAlive())
6478480093f4SDimitry Andric           process->Destroy(false);
6479480093f4SDimitry Andric       }
6480480093f4SDimitry Andric     }
6481480093f4SDimitry Andric       return MenuActionResult::Handled;
6482480093f4SDimitry Andric 
6483480093f4SDimitry Andric     case eMenuID_ProcessHalt: {
6484480093f4SDimitry Andric       ExecutionContext exe_ctx =
6485480093f4SDimitry Andric           m_debugger.GetCommandInterpreter().GetExecutionContext();
6486480093f4SDimitry Andric       if (exe_ctx.HasProcessScope()) {
6487480093f4SDimitry Andric         Process *process = exe_ctx.GetProcessPtr();
6488480093f4SDimitry Andric         if (process && process->IsAlive())
6489480093f4SDimitry Andric           process->Halt();
6490480093f4SDimitry Andric       }
6491480093f4SDimitry Andric     }
6492480093f4SDimitry Andric       return MenuActionResult::Handled;
6493480093f4SDimitry Andric 
6494e8d8bef9SDimitry Andric     case eMenuID_ProcessDetachResume:
6495e8d8bef9SDimitry Andric     case eMenuID_ProcessDetachSuspended: {
6496480093f4SDimitry Andric       ExecutionContext exe_ctx =
6497480093f4SDimitry Andric           m_debugger.GetCommandInterpreter().GetExecutionContext();
6498480093f4SDimitry Andric       if (exe_ctx.HasProcessScope()) {
6499480093f4SDimitry Andric         Process *process = exe_ctx.GetProcessPtr();
6500480093f4SDimitry Andric         if (process && process->IsAlive())
6501e8d8bef9SDimitry Andric           process->Detach(menu.GetIdentifier() ==
6502e8d8bef9SDimitry Andric                           eMenuID_ProcessDetachSuspended);
6503480093f4SDimitry Andric       }
6504480093f4SDimitry Andric     }
6505480093f4SDimitry Andric       return MenuActionResult::Handled;
6506480093f4SDimitry Andric 
6507480093f4SDimitry Andric     case eMenuID_Process: {
6508480093f4SDimitry Andric       // Populate the menu with all of the threads if the process is stopped
6509480093f4SDimitry Andric       // when the Process menu gets selected and is about to display its
6510480093f4SDimitry Andric       // submenu.
6511480093f4SDimitry Andric       Menus &submenus = menu.GetSubmenus();
6512480093f4SDimitry Andric       ExecutionContext exe_ctx =
6513480093f4SDimitry Andric           m_debugger.GetCommandInterpreter().GetExecutionContext();
6514480093f4SDimitry Andric       Process *process = exe_ctx.GetProcessPtr();
6515480093f4SDimitry Andric       if (process && process->IsAlive() &&
6516480093f4SDimitry Andric           StateIsStoppedState(process->GetState(), true)) {
6517480093f4SDimitry Andric         if (submenus.size() == 7)
6518480093f4SDimitry Andric           menu.AddSubmenu(MenuSP(new Menu(Menu::Type::Separator)));
6519480093f4SDimitry Andric         else if (submenus.size() > 8)
6520480093f4SDimitry Andric           submenus.erase(submenus.begin() + 8, submenus.end());
6521480093f4SDimitry Andric 
6522480093f4SDimitry Andric         ThreadList &threads = process->GetThreadList();
6523480093f4SDimitry Andric         std::lock_guard<std::recursive_mutex> guard(threads.GetMutex());
6524480093f4SDimitry Andric         size_t num_threads = threads.GetSize();
6525480093f4SDimitry Andric         for (size_t i = 0; i < num_threads; ++i) {
6526480093f4SDimitry Andric           ThreadSP thread_sp = threads.GetThreadAtIndex(i);
6527480093f4SDimitry Andric           char menu_char = '\0';
6528480093f4SDimitry Andric           if (i < 9)
6529480093f4SDimitry Andric             menu_char = '1' + i;
6530480093f4SDimitry Andric           StreamString thread_menu_title;
6531480093f4SDimitry Andric           thread_menu_title.Printf("Thread %u", thread_sp->GetIndexID());
6532480093f4SDimitry Andric           const char *thread_name = thread_sp->GetName();
6533480093f4SDimitry Andric           if (thread_name && thread_name[0])
6534480093f4SDimitry Andric             thread_menu_title.Printf(" %s", thread_name);
6535480093f4SDimitry Andric           else {
6536480093f4SDimitry Andric             const char *queue_name = thread_sp->GetQueueName();
6537480093f4SDimitry Andric             if (queue_name && queue_name[0])
6538480093f4SDimitry Andric               thread_menu_title.Printf(" %s", queue_name);
6539480093f4SDimitry Andric           }
6540480093f4SDimitry Andric           menu.AddSubmenu(
6541480093f4SDimitry Andric               MenuSP(new Menu(thread_menu_title.GetString().str().c_str(),
6542480093f4SDimitry Andric                               nullptr, menu_char, thread_sp->GetID())));
6543480093f4SDimitry Andric         }
6544480093f4SDimitry Andric       } else if (submenus.size() > 7) {
6545480093f4SDimitry Andric         // Remove the separator and any other thread submenu items that were
6546480093f4SDimitry Andric         // previously added
6547480093f4SDimitry Andric         submenus.erase(submenus.begin() + 7, submenus.end());
6548480093f4SDimitry Andric       }
6549349cc55cSDimitry Andric       // Since we are adding and removing items we need to recalculate the
6550349cc55cSDimitry Andric       // name lengths
6551480093f4SDimitry Andric       menu.RecalculateNameLengths();
6552480093f4SDimitry Andric     }
6553480093f4SDimitry Andric       return MenuActionResult::Handled;
6554480093f4SDimitry Andric 
6555480093f4SDimitry Andric     case eMenuID_ViewVariables: {
6556480093f4SDimitry Andric       WindowSP main_window_sp = m_app.GetMainWindow();
6557480093f4SDimitry Andric       WindowSP source_window_sp = main_window_sp->FindSubWindow("Source");
6558480093f4SDimitry Andric       WindowSP variables_window_sp = main_window_sp->FindSubWindow("Variables");
6559480093f4SDimitry Andric       WindowSP registers_window_sp = main_window_sp->FindSubWindow("Registers");
6560480093f4SDimitry Andric       const Rect source_bounds = source_window_sp->GetBounds();
6561480093f4SDimitry Andric 
6562480093f4SDimitry Andric       if (variables_window_sp) {
6563480093f4SDimitry Andric         const Rect variables_bounds = variables_window_sp->GetBounds();
6564480093f4SDimitry Andric 
6565480093f4SDimitry Andric         main_window_sp->RemoveSubWindow(variables_window_sp.get());
6566480093f4SDimitry Andric 
6567480093f4SDimitry Andric         if (registers_window_sp) {
6568480093f4SDimitry Andric           // We have a registers window, so give all the area back to the
6569480093f4SDimitry Andric           // registers window
6570480093f4SDimitry Andric           Rect registers_bounds = variables_bounds;
6571480093f4SDimitry Andric           registers_bounds.size.width = source_bounds.size.width;
6572480093f4SDimitry Andric           registers_window_sp->SetBounds(registers_bounds);
6573480093f4SDimitry Andric         } else {
6574480093f4SDimitry Andric           // We have no registers window showing so give the bottom area back
6575480093f4SDimitry Andric           // to the source view
6576480093f4SDimitry Andric           source_window_sp->Resize(source_bounds.size.width,
6577480093f4SDimitry Andric                                    source_bounds.size.height +
6578480093f4SDimitry Andric                                        variables_bounds.size.height);
6579480093f4SDimitry Andric         }
6580480093f4SDimitry Andric       } else {
6581480093f4SDimitry Andric         Rect new_variables_rect;
6582480093f4SDimitry Andric         if (registers_window_sp) {
6583480093f4SDimitry Andric           // We have a registers window so split the area of the registers
6584480093f4SDimitry Andric           // window into two columns where the left hand side will be the
6585480093f4SDimitry Andric           // variables and the right hand side will be the registers
6586480093f4SDimitry Andric           const Rect variables_bounds = registers_window_sp->GetBounds();
6587480093f4SDimitry Andric           Rect new_registers_rect;
6588480093f4SDimitry Andric           variables_bounds.VerticalSplitPercentage(0.50, new_variables_rect,
6589480093f4SDimitry Andric                                                    new_registers_rect);
6590480093f4SDimitry Andric           registers_window_sp->SetBounds(new_registers_rect);
6591480093f4SDimitry Andric         } else {
6592e8d8bef9SDimitry Andric           // No registers window, grab the bottom part of the source window
6593480093f4SDimitry Andric           Rect new_source_rect;
6594480093f4SDimitry Andric           source_bounds.HorizontalSplitPercentage(0.70, new_source_rect,
6595480093f4SDimitry Andric                                                   new_variables_rect);
6596480093f4SDimitry Andric           source_window_sp->SetBounds(new_source_rect);
6597480093f4SDimitry Andric         }
6598480093f4SDimitry Andric         WindowSP new_window_sp = main_window_sp->CreateSubWindow(
6599480093f4SDimitry Andric             "Variables", new_variables_rect, false);
6600480093f4SDimitry Andric         new_window_sp->SetDelegate(
6601480093f4SDimitry Andric             WindowDelegateSP(new FrameVariablesWindowDelegate(m_debugger)));
6602480093f4SDimitry Andric       }
6603480093f4SDimitry Andric       touchwin(stdscr);
6604480093f4SDimitry Andric     }
6605480093f4SDimitry Andric       return MenuActionResult::Handled;
6606480093f4SDimitry Andric 
6607480093f4SDimitry Andric     case eMenuID_ViewRegisters: {
6608480093f4SDimitry Andric       WindowSP main_window_sp = m_app.GetMainWindow();
6609480093f4SDimitry Andric       WindowSP source_window_sp = main_window_sp->FindSubWindow("Source");
6610480093f4SDimitry Andric       WindowSP variables_window_sp = main_window_sp->FindSubWindow("Variables");
6611480093f4SDimitry Andric       WindowSP registers_window_sp = main_window_sp->FindSubWindow("Registers");
6612480093f4SDimitry Andric       const Rect source_bounds = source_window_sp->GetBounds();
6613480093f4SDimitry Andric 
6614480093f4SDimitry Andric       if (registers_window_sp) {
6615480093f4SDimitry Andric         if (variables_window_sp) {
6616480093f4SDimitry Andric           const Rect variables_bounds = variables_window_sp->GetBounds();
6617480093f4SDimitry Andric 
6618480093f4SDimitry Andric           // We have a variables window, so give all the area back to the
6619480093f4SDimitry Andric           // variables window
6620480093f4SDimitry Andric           variables_window_sp->Resize(variables_bounds.size.width +
6621480093f4SDimitry Andric                                           registers_window_sp->GetWidth(),
6622480093f4SDimitry Andric                                       variables_bounds.size.height);
6623480093f4SDimitry Andric         } else {
6624480093f4SDimitry Andric           // We have no variables window showing so give the bottom area back
6625480093f4SDimitry Andric           // to the source view
6626480093f4SDimitry Andric           source_window_sp->Resize(source_bounds.size.width,
6627480093f4SDimitry Andric                                    source_bounds.size.height +
6628480093f4SDimitry Andric                                        registers_window_sp->GetHeight());
6629480093f4SDimitry Andric         }
6630480093f4SDimitry Andric         main_window_sp->RemoveSubWindow(registers_window_sp.get());
6631480093f4SDimitry Andric       } else {
6632480093f4SDimitry Andric         Rect new_regs_rect;
6633480093f4SDimitry Andric         if (variables_window_sp) {
6634480093f4SDimitry Andric           // We have a variables window, split it into two columns where the
6635480093f4SDimitry Andric           // left hand side will be the variables and the right hand side will
6636480093f4SDimitry Andric           // be the registers
6637480093f4SDimitry Andric           const Rect variables_bounds = variables_window_sp->GetBounds();
6638480093f4SDimitry Andric           Rect new_vars_rect;
6639480093f4SDimitry Andric           variables_bounds.VerticalSplitPercentage(0.50, new_vars_rect,
6640480093f4SDimitry Andric                                                    new_regs_rect);
6641480093f4SDimitry Andric           variables_window_sp->SetBounds(new_vars_rect);
6642480093f4SDimitry Andric         } else {
6643e8d8bef9SDimitry Andric           // No variables window, grab the bottom part of the source window
6644480093f4SDimitry Andric           Rect new_source_rect;
6645480093f4SDimitry Andric           source_bounds.HorizontalSplitPercentage(0.70, new_source_rect,
6646480093f4SDimitry Andric                                                   new_regs_rect);
6647480093f4SDimitry Andric           source_window_sp->SetBounds(new_source_rect);
6648480093f4SDimitry Andric         }
6649480093f4SDimitry Andric         WindowSP new_window_sp =
6650480093f4SDimitry Andric             main_window_sp->CreateSubWindow("Registers", new_regs_rect, false);
6651480093f4SDimitry Andric         new_window_sp->SetDelegate(
6652480093f4SDimitry Andric             WindowDelegateSP(new RegistersWindowDelegate(m_debugger)));
6653480093f4SDimitry Andric       }
6654480093f4SDimitry Andric       touchwin(stdscr);
6655480093f4SDimitry Andric     }
6656480093f4SDimitry Andric       return MenuActionResult::Handled;
6657480093f4SDimitry Andric 
6658349cc55cSDimitry Andric     case eMenuID_ViewBreakpoints: {
6659349cc55cSDimitry Andric       WindowSP main_window_sp = m_app.GetMainWindow();
6660349cc55cSDimitry Andric       WindowSP threads_window_sp = main_window_sp->FindSubWindow("Threads");
6661349cc55cSDimitry Andric       WindowSP breakpoints_window_sp =
6662349cc55cSDimitry Andric           main_window_sp->FindSubWindow("Breakpoints");
6663349cc55cSDimitry Andric       const Rect threads_bounds = threads_window_sp->GetBounds();
6664349cc55cSDimitry Andric 
6665349cc55cSDimitry Andric       // If a breakpoints window already exists, remove it and give the area
6666349cc55cSDimitry Andric       // it used to occupy to the threads window. If it doesn't exist, split
6667349cc55cSDimitry Andric       // the threads window horizontally into two windows where the top window
6668349cc55cSDimitry Andric       // is the threads window and the bottom window is a newly added
6669349cc55cSDimitry Andric       // breakpoints window.
6670349cc55cSDimitry Andric       if (breakpoints_window_sp) {
6671349cc55cSDimitry Andric         threads_window_sp->Resize(threads_bounds.size.width,
6672349cc55cSDimitry Andric                                   threads_bounds.size.height +
6673349cc55cSDimitry Andric                                       breakpoints_window_sp->GetHeight());
6674349cc55cSDimitry Andric         main_window_sp->RemoveSubWindow(breakpoints_window_sp.get());
6675349cc55cSDimitry Andric       } else {
6676349cc55cSDimitry Andric         Rect new_threads_bounds, breakpoints_bounds;
6677349cc55cSDimitry Andric         threads_bounds.HorizontalSplitPercentage(0.70, new_threads_bounds,
6678349cc55cSDimitry Andric                                                  breakpoints_bounds);
6679349cc55cSDimitry Andric         threads_window_sp->SetBounds(new_threads_bounds);
6680349cc55cSDimitry Andric         breakpoints_window_sp = main_window_sp->CreateSubWindow(
6681349cc55cSDimitry Andric             "Breakpoints", breakpoints_bounds, false);
6682349cc55cSDimitry Andric         TreeDelegateSP breakpoints_delegate_sp(
6683349cc55cSDimitry Andric             new BreakpointsTreeDelegate(m_debugger));
6684349cc55cSDimitry Andric         breakpoints_window_sp->SetDelegate(WindowDelegateSP(
6685349cc55cSDimitry Andric             new TreeWindowDelegate(m_debugger, breakpoints_delegate_sp)));
6686349cc55cSDimitry Andric       }
6687349cc55cSDimitry Andric       touchwin(stdscr);
6688349cc55cSDimitry Andric       return MenuActionResult::Handled;
6689349cc55cSDimitry Andric     }
6690349cc55cSDimitry Andric 
6691480093f4SDimitry Andric     case eMenuID_HelpGUIHelp:
6692480093f4SDimitry Andric       m_app.GetMainWindow()->CreateHelpSubwindow();
6693480093f4SDimitry Andric       return MenuActionResult::Handled;
6694480093f4SDimitry Andric 
6695480093f4SDimitry Andric     default:
6696480093f4SDimitry Andric       break;
6697480093f4SDimitry Andric     }
6698480093f4SDimitry Andric 
6699480093f4SDimitry Andric     return MenuActionResult::NotHandled;
6700480093f4SDimitry Andric   }
6701480093f4SDimitry Andric 
6702480093f4SDimitry Andric protected:
6703480093f4SDimitry Andric   Application &m_app;
6704480093f4SDimitry Andric   Debugger &m_debugger;
6705480093f4SDimitry Andric };
6706480093f4SDimitry Andric 
6707480093f4SDimitry Andric class StatusBarWindowDelegate : public WindowDelegate {
6708480093f4SDimitry Andric public:
6709480093f4SDimitry Andric   StatusBarWindowDelegate(Debugger &debugger) : m_debugger(debugger) {
6710480093f4SDimitry Andric     FormatEntity::Parse("Thread: ${thread.id%tid}", m_format);
6711480093f4SDimitry Andric   }
6712480093f4SDimitry Andric 
6713480093f4SDimitry Andric   ~StatusBarWindowDelegate() override = default;
6714480093f4SDimitry Andric 
6715480093f4SDimitry Andric   bool WindowDelegateDraw(Window &window, bool force) override {
6716480093f4SDimitry Andric     ExecutionContext exe_ctx =
6717480093f4SDimitry Andric         m_debugger.GetCommandInterpreter().GetExecutionContext();
6718480093f4SDimitry Andric     Process *process = exe_ctx.GetProcessPtr();
6719480093f4SDimitry Andric     Thread *thread = exe_ctx.GetThreadPtr();
6720480093f4SDimitry Andric     StackFrame *frame = exe_ctx.GetFramePtr();
6721480093f4SDimitry Andric     window.Erase();
6722e8d8bef9SDimitry Andric     window.SetBackground(BlackOnWhite);
6723480093f4SDimitry Andric     window.MoveCursor(0, 0);
6724480093f4SDimitry Andric     if (process) {
6725480093f4SDimitry Andric       const StateType state = process->GetState();
6726480093f4SDimitry Andric       window.Printf("Process: %5" PRIu64 " %10s", process->GetID(),
6727480093f4SDimitry Andric                     StateAsCString(state));
6728480093f4SDimitry Andric 
6729480093f4SDimitry Andric       if (StateIsStoppedState(state, true)) {
6730480093f4SDimitry Andric         StreamString strm;
6731480093f4SDimitry Andric         if (thread && FormatEntity::Format(m_format, strm, nullptr, &exe_ctx,
6732480093f4SDimitry Andric                                            nullptr, nullptr, false, false)) {
6733480093f4SDimitry Andric           window.MoveCursor(40, 0);
6734e8d8bef9SDimitry Andric           window.PutCStringTruncated(1, strm.GetString().str().c_str());
6735480093f4SDimitry Andric         }
6736480093f4SDimitry Andric 
6737480093f4SDimitry Andric         window.MoveCursor(60, 0);
6738480093f4SDimitry Andric         if (frame)
6739480093f4SDimitry Andric           window.Printf("Frame: %3u  PC = 0x%16.16" PRIx64,
6740480093f4SDimitry Andric                         frame->GetFrameIndex(),
6741480093f4SDimitry Andric                         frame->GetFrameCodeAddress().GetOpcodeLoadAddress(
6742480093f4SDimitry Andric                             exe_ctx.GetTargetPtr()));
6743480093f4SDimitry Andric       } else if (state == eStateExited) {
6744480093f4SDimitry Andric         const char *exit_desc = process->GetExitDescription();
6745480093f4SDimitry Andric         const int exit_status = process->GetExitStatus();
6746480093f4SDimitry Andric         if (exit_desc && exit_desc[0])
6747480093f4SDimitry Andric           window.Printf(" with status = %i (%s)", exit_status, exit_desc);
6748480093f4SDimitry Andric         else
6749480093f4SDimitry Andric           window.Printf(" with status = %i", exit_status);
6750480093f4SDimitry Andric       }
6751480093f4SDimitry Andric     }
6752480093f4SDimitry Andric     return true;
6753480093f4SDimitry Andric   }
6754480093f4SDimitry Andric 
6755480093f4SDimitry Andric protected:
6756480093f4SDimitry Andric   Debugger &m_debugger;
6757480093f4SDimitry Andric   FormatEntity::Entry m_format;
6758480093f4SDimitry Andric };
6759480093f4SDimitry Andric 
6760480093f4SDimitry Andric class SourceFileWindowDelegate : public WindowDelegate {
6761480093f4SDimitry Andric public:
6762480093f4SDimitry Andric   SourceFileWindowDelegate(Debugger &debugger)
6763480093f4SDimitry Andric       : WindowDelegate(), m_debugger(debugger), m_sc(), m_file_sp(),
676481ad6265SDimitry Andric         m_disassembly_sp(), m_disassembly_range(), m_title() {}
6765480093f4SDimitry Andric 
6766480093f4SDimitry Andric   ~SourceFileWindowDelegate() override = default;
6767480093f4SDimitry Andric 
6768480093f4SDimitry Andric   void Update(const SymbolContext &sc) { m_sc = sc; }
6769480093f4SDimitry Andric 
6770480093f4SDimitry Andric   uint32_t NumVisibleLines() const { return m_max_y - m_min_y; }
6771480093f4SDimitry Andric 
6772480093f4SDimitry Andric   const char *WindowDelegateGetHelpText() override {
6773480093f4SDimitry Andric     return "Source/Disassembly window keyboard shortcuts:";
6774480093f4SDimitry Andric   }
6775480093f4SDimitry Andric 
6776480093f4SDimitry Andric   KeyHelp *WindowDelegateGetKeyHelp() override {
6777480093f4SDimitry Andric     static curses::KeyHelp g_source_view_key_help[] = {
6778480093f4SDimitry Andric         {KEY_RETURN, "Run to selected line with one shot breakpoint"},
6779480093f4SDimitry Andric         {KEY_UP, "Select previous source line"},
6780480093f4SDimitry Andric         {KEY_DOWN, "Select next source line"},
6781e8d8bef9SDimitry Andric         {KEY_LEFT, "Scroll to the left"},
6782e8d8bef9SDimitry Andric         {KEY_RIGHT, "Scroll to the right"},
6783480093f4SDimitry Andric         {KEY_PPAGE, "Page up"},
6784480093f4SDimitry Andric         {KEY_NPAGE, "Page down"},
6785480093f4SDimitry Andric         {'b', "Set breakpoint on selected source/disassembly line"},
6786480093f4SDimitry Andric         {'c', "Continue process"},
6787480093f4SDimitry Andric         {'D', "Detach with process suspended"},
6788480093f4SDimitry Andric         {'h', "Show help dialog"},
6789480093f4SDimitry Andric         {'n', "Step over (source line)"},
6790480093f4SDimitry Andric         {'N', "Step over (single instruction)"},
6791e8d8bef9SDimitry Andric         {'f', "Step out (finish)"},
6792480093f4SDimitry Andric         {'s', "Step in (source line)"},
6793480093f4SDimitry Andric         {'S', "Step in (single instruction)"},
6794e8d8bef9SDimitry Andric         {'u', "Frame up"},
6795e8d8bef9SDimitry Andric         {'d', "Frame down"},
6796480093f4SDimitry Andric         {',', "Page up"},
6797480093f4SDimitry Andric         {'.', "Page down"},
6798480093f4SDimitry Andric         {'\0', nullptr}};
6799480093f4SDimitry Andric     return g_source_view_key_help;
6800480093f4SDimitry Andric   }
6801480093f4SDimitry Andric 
6802480093f4SDimitry Andric   bool WindowDelegateDraw(Window &window, bool force) override {
6803480093f4SDimitry Andric     ExecutionContext exe_ctx =
6804480093f4SDimitry Andric         m_debugger.GetCommandInterpreter().GetExecutionContext();
6805480093f4SDimitry Andric     Process *process = exe_ctx.GetProcessPtr();
6806480093f4SDimitry Andric     Thread *thread = nullptr;
6807480093f4SDimitry Andric 
6808480093f4SDimitry Andric     bool update_location = false;
6809480093f4SDimitry Andric     if (process) {
6810480093f4SDimitry Andric       StateType state = process->GetState();
6811480093f4SDimitry Andric       if (StateIsStoppedState(state, true)) {
6812480093f4SDimitry Andric         // We are stopped, so it is ok to
6813480093f4SDimitry Andric         update_location = true;
6814480093f4SDimitry Andric       }
6815480093f4SDimitry Andric     }
6816480093f4SDimitry Andric 
6817480093f4SDimitry Andric     m_min_x = 1;
6818480093f4SDimitry Andric     m_min_y = 2;
6819480093f4SDimitry Andric     m_max_x = window.GetMaxX() - 1;
6820480093f4SDimitry Andric     m_max_y = window.GetMaxY() - 1;
6821480093f4SDimitry Andric 
6822480093f4SDimitry Andric     const uint32_t num_visible_lines = NumVisibleLines();
6823480093f4SDimitry Andric     StackFrameSP frame_sp;
6824480093f4SDimitry Andric     bool set_selected_line_to_pc = false;
6825480093f4SDimitry Andric 
6826480093f4SDimitry Andric     if (update_location) {
6827bdd1243dSDimitry Andric       const bool process_alive = process->IsAlive();
6828480093f4SDimitry Andric       bool thread_changed = false;
6829480093f4SDimitry Andric       if (process_alive) {
6830480093f4SDimitry Andric         thread = exe_ctx.GetThreadPtr();
6831480093f4SDimitry Andric         if (thread) {
6832*06c3fb27SDimitry Andric           frame_sp = thread->GetSelectedFrame(SelectMostRelevantFrame);
6833480093f4SDimitry Andric           auto tid = thread->GetID();
6834480093f4SDimitry Andric           thread_changed = tid != m_tid;
6835480093f4SDimitry Andric           m_tid = tid;
6836480093f4SDimitry Andric         } else {
6837480093f4SDimitry Andric           if (m_tid != LLDB_INVALID_THREAD_ID) {
6838480093f4SDimitry Andric             thread_changed = true;
6839480093f4SDimitry Andric             m_tid = LLDB_INVALID_THREAD_ID;
6840480093f4SDimitry Andric           }
6841480093f4SDimitry Andric         }
6842480093f4SDimitry Andric       }
6843480093f4SDimitry Andric       const uint32_t stop_id = process ? process->GetStopID() : 0;
6844480093f4SDimitry Andric       const bool stop_id_changed = stop_id != m_stop_id;
6845480093f4SDimitry Andric       bool frame_changed = false;
6846480093f4SDimitry Andric       m_stop_id = stop_id;
6847480093f4SDimitry Andric       m_title.Clear();
6848480093f4SDimitry Andric       if (frame_sp) {
6849480093f4SDimitry Andric         m_sc = frame_sp->GetSymbolContext(eSymbolContextEverything);
6850480093f4SDimitry Andric         if (m_sc.module_sp) {
6851480093f4SDimitry Andric           m_title.Printf(
6852480093f4SDimitry Andric               "%s", m_sc.module_sp->GetFileSpec().GetFilename().GetCString());
6853480093f4SDimitry Andric           ConstString func_name = m_sc.GetFunctionName();
6854480093f4SDimitry Andric           if (func_name)
6855480093f4SDimitry Andric             m_title.Printf("`%s", func_name.GetCString());
6856480093f4SDimitry Andric         }
6857480093f4SDimitry Andric         const uint32_t frame_idx = frame_sp->GetFrameIndex();
6858480093f4SDimitry Andric         frame_changed = frame_idx != m_frame_idx;
6859480093f4SDimitry Andric         m_frame_idx = frame_idx;
6860480093f4SDimitry Andric       } else {
6861480093f4SDimitry Andric         m_sc.Clear(true);
6862480093f4SDimitry Andric         frame_changed = m_frame_idx != UINT32_MAX;
6863480093f4SDimitry Andric         m_frame_idx = UINT32_MAX;
6864480093f4SDimitry Andric       }
6865480093f4SDimitry Andric 
6866480093f4SDimitry Andric       const bool context_changed =
6867480093f4SDimitry Andric           thread_changed || frame_changed || stop_id_changed;
6868480093f4SDimitry Andric 
6869480093f4SDimitry Andric       if (process_alive) {
6870480093f4SDimitry Andric         if (m_sc.line_entry.IsValid()) {
6871480093f4SDimitry Andric           m_pc_line = m_sc.line_entry.line;
6872480093f4SDimitry Andric           if (m_pc_line != UINT32_MAX)
6873480093f4SDimitry Andric             --m_pc_line; // Convert to zero based line number...
6874480093f4SDimitry Andric           // Update the selected line if the stop ID changed...
6875480093f4SDimitry Andric           if (context_changed)
6876480093f4SDimitry Andric             m_selected_line = m_pc_line;
6877480093f4SDimitry Andric 
6878480093f4SDimitry Andric           if (m_file_sp && m_file_sp->GetFileSpec() == m_sc.line_entry.file) {
6879349cc55cSDimitry Andric             // Same file, nothing to do, we should either have the lines or
6880349cc55cSDimitry Andric             // not (source file missing)
6881480093f4SDimitry Andric             if (m_selected_line >= static_cast<size_t>(m_first_visible_line)) {
6882480093f4SDimitry Andric               if (m_selected_line >= m_first_visible_line + num_visible_lines)
6883480093f4SDimitry Andric                 m_first_visible_line = m_selected_line - 10;
6884480093f4SDimitry Andric             } else {
6885480093f4SDimitry Andric               if (m_selected_line > 10)
6886480093f4SDimitry Andric                 m_first_visible_line = m_selected_line - 10;
6887480093f4SDimitry Andric               else
6888480093f4SDimitry Andric                 m_first_visible_line = 0;
6889480093f4SDimitry Andric             }
6890480093f4SDimitry Andric           } else {
6891480093f4SDimitry Andric             // File changed, set selected line to the line with the PC
6892480093f4SDimitry Andric             m_selected_line = m_pc_line;
6893480093f4SDimitry Andric             m_file_sp =
6894480093f4SDimitry Andric                 m_debugger.GetSourceManager().GetFile(m_sc.line_entry.file);
6895480093f4SDimitry Andric             if (m_file_sp) {
6896480093f4SDimitry Andric               const size_t num_lines = m_file_sp->GetNumLines();
6897480093f4SDimitry Andric               m_line_width = 1;
6898480093f4SDimitry Andric               for (size_t n = num_lines; n >= 10; n = n / 10)
6899480093f4SDimitry Andric                 ++m_line_width;
6900480093f4SDimitry Andric 
6901480093f4SDimitry Andric               if (num_lines < num_visible_lines ||
6902480093f4SDimitry Andric                   m_selected_line < num_visible_lines)
6903480093f4SDimitry Andric                 m_first_visible_line = 0;
6904480093f4SDimitry Andric               else
6905480093f4SDimitry Andric                 m_first_visible_line = m_selected_line - 10;
6906480093f4SDimitry Andric             }
6907480093f4SDimitry Andric           }
6908480093f4SDimitry Andric         } else {
6909480093f4SDimitry Andric           m_file_sp.reset();
6910480093f4SDimitry Andric         }
6911480093f4SDimitry Andric 
6912480093f4SDimitry Andric         if (!m_file_sp || m_file_sp->GetNumLines() == 0) {
6913480093f4SDimitry Andric           // Show disassembly
6914480093f4SDimitry Andric           bool prefer_file_cache = false;
6915480093f4SDimitry Andric           if (m_sc.function) {
6916480093f4SDimitry Andric             if (m_disassembly_scope != m_sc.function) {
6917480093f4SDimitry Andric               m_disassembly_scope = m_sc.function;
6918480093f4SDimitry Andric               m_disassembly_sp = m_sc.function->GetInstructions(
6919fe6060f1SDimitry Andric                   exe_ctx, nullptr, !prefer_file_cache);
6920480093f4SDimitry Andric               if (m_disassembly_sp) {
6921480093f4SDimitry Andric                 set_selected_line_to_pc = true;
6922480093f4SDimitry Andric                 m_disassembly_range = m_sc.function->GetAddressRange();
6923480093f4SDimitry Andric               } else {
6924480093f4SDimitry Andric                 m_disassembly_range.Clear();
6925480093f4SDimitry Andric               }
6926480093f4SDimitry Andric             } else {
6927480093f4SDimitry Andric               set_selected_line_to_pc = context_changed;
6928480093f4SDimitry Andric             }
6929480093f4SDimitry Andric           } else if (m_sc.symbol) {
6930480093f4SDimitry Andric             if (m_disassembly_scope != m_sc.symbol) {
6931480093f4SDimitry Andric               m_disassembly_scope = m_sc.symbol;
6932480093f4SDimitry Andric               m_disassembly_sp = m_sc.symbol->GetInstructions(
6933480093f4SDimitry Andric                   exe_ctx, nullptr, prefer_file_cache);
6934480093f4SDimitry Andric               if (m_disassembly_sp) {
6935480093f4SDimitry Andric                 set_selected_line_to_pc = true;
6936480093f4SDimitry Andric                 m_disassembly_range.GetBaseAddress() =
6937480093f4SDimitry Andric                     m_sc.symbol->GetAddress();
6938480093f4SDimitry Andric                 m_disassembly_range.SetByteSize(m_sc.symbol->GetByteSize());
6939480093f4SDimitry Andric               } else {
6940480093f4SDimitry Andric                 m_disassembly_range.Clear();
6941480093f4SDimitry Andric               }
6942480093f4SDimitry Andric             } else {
6943480093f4SDimitry Andric               set_selected_line_to_pc = context_changed;
6944480093f4SDimitry Andric             }
6945480093f4SDimitry Andric           }
6946480093f4SDimitry Andric         }
6947480093f4SDimitry Andric       } else {
6948480093f4SDimitry Andric         m_pc_line = UINT32_MAX;
6949480093f4SDimitry Andric       }
6950480093f4SDimitry Andric     }
6951480093f4SDimitry Andric 
6952480093f4SDimitry Andric     const int window_width = window.GetWidth();
6953480093f4SDimitry Andric     window.Erase();
6954480093f4SDimitry Andric     window.DrawTitleBox("Sources");
6955480093f4SDimitry Andric     if (!m_title.GetString().empty()) {
6956480093f4SDimitry Andric       window.AttributeOn(A_REVERSE);
6957480093f4SDimitry Andric       window.MoveCursor(1, 1);
6958480093f4SDimitry Andric       window.PutChar(' ');
6959e8d8bef9SDimitry Andric       window.PutCStringTruncated(1, m_title.GetString().str().c_str());
6960480093f4SDimitry Andric       int x = window.GetCursorX();
6961480093f4SDimitry Andric       if (x < window_width - 1) {
6962480093f4SDimitry Andric         window.Printf("%*s", window_width - x - 1, "");
6963480093f4SDimitry Andric       }
6964480093f4SDimitry Andric       window.AttributeOff(A_REVERSE);
6965480093f4SDimitry Andric     }
6966480093f4SDimitry Andric 
6967480093f4SDimitry Andric     Target *target = exe_ctx.GetTargetPtr();
6968480093f4SDimitry Andric     const size_t num_source_lines = GetNumSourceLines();
6969480093f4SDimitry Andric     if (num_source_lines > 0) {
6970480093f4SDimitry Andric       // Display source
6971480093f4SDimitry Andric       BreakpointLines bp_lines;
6972480093f4SDimitry Andric       if (target) {
6973480093f4SDimitry Andric         BreakpointList &bp_list = target->GetBreakpointList();
6974480093f4SDimitry Andric         const size_t num_bps = bp_list.GetSize();
6975480093f4SDimitry Andric         for (size_t bp_idx = 0; bp_idx < num_bps; ++bp_idx) {
6976480093f4SDimitry Andric           BreakpointSP bp_sp = bp_list.GetBreakpointAtIndex(bp_idx);
6977480093f4SDimitry Andric           const size_t num_bps_locs = bp_sp->GetNumLocations();
6978480093f4SDimitry Andric           for (size_t bp_loc_idx = 0; bp_loc_idx < num_bps_locs; ++bp_loc_idx) {
6979480093f4SDimitry Andric             BreakpointLocationSP bp_loc_sp =
6980480093f4SDimitry Andric                 bp_sp->GetLocationAtIndex(bp_loc_idx);
6981480093f4SDimitry Andric             LineEntry bp_loc_line_entry;
6982480093f4SDimitry Andric             if (bp_loc_sp->GetAddress().CalculateSymbolContextLineEntry(
6983480093f4SDimitry Andric                     bp_loc_line_entry)) {
6984480093f4SDimitry Andric               if (m_file_sp->GetFileSpec() == bp_loc_line_entry.file) {
6985480093f4SDimitry Andric                 bp_lines.insert(bp_loc_line_entry.line);
6986480093f4SDimitry Andric               }
6987480093f4SDimitry Andric             }
6988480093f4SDimitry Andric           }
6989480093f4SDimitry Andric         }
6990480093f4SDimitry Andric       }
6991480093f4SDimitry Andric 
6992480093f4SDimitry Andric       for (size_t i = 0; i < num_visible_lines; ++i) {
6993480093f4SDimitry Andric         const uint32_t curr_line = m_first_visible_line + i;
6994480093f4SDimitry Andric         if (curr_line < num_source_lines) {
6995480093f4SDimitry Andric           const int line_y = m_min_y + i;
6996480093f4SDimitry Andric           window.MoveCursor(1, line_y);
6997480093f4SDimitry Andric           const bool is_pc_line = curr_line == m_pc_line;
6998480093f4SDimitry Andric           const bool line_is_selected = m_selected_line == curr_line;
699981ad6265SDimitry Andric           // Highlight the line as the PC line first (done by passing
700081ad6265SDimitry Andric           // argument to OutputColoredStringTruncated()), then if the selected
700181ad6265SDimitry Andric           // line isn't the same as the PC line, highlight it differently.
7002480093f4SDimitry Andric           attr_t highlight_attr = 0;
7003480093f4SDimitry Andric           attr_t bp_attr = 0;
700481ad6265SDimitry Andric           if (line_is_selected && !is_pc_line)
700581ad6265SDimitry Andric             highlight_attr = A_REVERSE;
7006480093f4SDimitry Andric 
7007480093f4SDimitry Andric           if (bp_lines.find(curr_line + 1) != bp_lines.end())
7008e8d8bef9SDimitry Andric             bp_attr = COLOR_PAIR(BlackOnWhite);
7009480093f4SDimitry Andric 
7010480093f4SDimitry Andric           if (bp_attr)
7011480093f4SDimitry Andric             window.AttributeOn(bp_attr);
7012480093f4SDimitry Andric 
7013480093f4SDimitry Andric           window.Printf(" %*u ", m_line_width, curr_line + 1);
7014480093f4SDimitry Andric 
7015480093f4SDimitry Andric           if (bp_attr)
7016480093f4SDimitry Andric             window.AttributeOff(bp_attr);
7017480093f4SDimitry Andric 
7018480093f4SDimitry Andric           window.PutChar(ACS_VLINE);
7019480093f4SDimitry Andric           // Mark the line with the PC with a diamond
7020480093f4SDimitry Andric           if (is_pc_line)
7021480093f4SDimitry Andric             window.PutChar(ACS_DIAMOND);
7022480093f4SDimitry Andric           else
7023480093f4SDimitry Andric             window.PutChar(' ');
7024480093f4SDimitry Andric 
7025480093f4SDimitry Andric           if (highlight_attr)
7026480093f4SDimitry Andric             window.AttributeOn(highlight_attr);
7027e8d8bef9SDimitry Andric 
7028e8d8bef9SDimitry Andric           StreamString lineStream;
702981ad6265SDimitry Andric 
7030bdd1243dSDimitry Andric           std::optional<size_t> column;
703181ad6265SDimitry Andric           if (is_pc_line && m_sc.line_entry.IsValid() && m_sc.line_entry.column)
703281ad6265SDimitry Andric             column = m_sc.line_entry.column - 1;
703381ad6265SDimitry Andric           m_file_sp->DisplaySourceLines(curr_line + 1, column, 0, 0,
703481ad6265SDimitry Andric                                         &lineStream);
7035e8d8bef9SDimitry Andric           StringRef line = lineStream.GetString();
7036e8d8bef9SDimitry Andric           if (line.endswith("\n"))
7037e8d8bef9SDimitry Andric             line = line.drop_back();
7038e8d8bef9SDimitry Andric           bool wasWritten = window.OutputColoredStringTruncated(
703981ad6265SDimitry Andric               1, line, m_first_visible_column, is_pc_line);
704081ad6265SDimitry Andric           if (!wasWritten && (line_is_selected || is_pc_line)) {
704181ad6265SDimitry Andric             // Draw an empty space to show the selected/PC line if empty,
7042e8d8bef9SDimitry Andric             // or draw '<' if nothing is visible because of scrolling too much
7043e8d8bef9SDimitry Andric             // to the right.
7044e8d8bef9SDimitry Andric             window.PutCStringTruncated(
7045e8d8bef9SDimitry Andric                 1, line.empty() && m_first_visible_column == 0 ? " " : "<");
7046e8d8bef9SDimitry Andric           }
7047480093f4SDimitry Andric 
7048480093f4SDimitry Andric           if (is_pc_line && frame_sp &&
7049480093f4SDimitry Andric               frame_sp->GetConcreteFrameIndex() == 0) {
7050480093f4SDimitry Andric             StopInfoSP stop_info_sp;
7051480093f4SDimitry Andric             if (thread)
7052480093f4SDimitry Andric               stop_info_sp = thread->GetStopInfo();
7053480093f4SDimitry Andric             if (stop_info_sp) {
7054480093f4SDimitry Andric               const char *stop_description = stop_info_sp->GetDescription();
7055480093f4SDimitry Andric               if (stop_description && stop_description[0]) {
7056480093f4SDimitry Andric                 size_t stop_description_len = strlen(stop_description);
7057480093f4SDimitry Andric                 int desc_x = window_width - stop_description_len - 16;
7058e8d8bef9SDimitry Andric                 if (desc_x - window.GetCursorX() > 0)
7059480093f4SDimitry Andric                   window.Printf("%*s", desc_x - window.GetCursorX(), "");
7060e8d8bef9SDimitry Andric                 window.MoveCursor(window_width - stop_description_len - 16,
7061e8d8bef9SDimitry Andric                                   line_y);
7062e8d8bef9SDimitry Andric                 const attr_t stop_reason_attr = COLOR_PAIR(WhiteOnBlue);
7063e8d8bef9SDimitry Andric                 window.AttributeOn(stop_reason_attr);
7064e8d8bef9SDimitry Andric                 window.PrintfTruncated(1, " <<< Thread %u: %s ",
7065e8d8bef9SDimitry Andric                                        thread->GetIndexID(), stop_description);
7066e8d8bef9SDimitry Andric                 window.AttributeOff(stop_reason_attr);
7067480093f4SDimitry Andric               }
7068480093f4SDimitry Andric             } else {
7069480093f4SDimitry Andric               window.Printf("%*s", window_width - window.GetCursorX() - 1, "");
7070480093f4SDimitry Andric             }
7071480093f4SDimitry Andric           }
7072480093f4SDimitry Andric           if (highlight_attr)
7073480093f4SDimitry Andric             window.AttributeOff(highlight_attr);
7074480093f4SDimitry Andric         } else {
7075480093f4SDimitry Andric           break;
7076480093f4SDimitry Andric         }
7077480093f4SDimitry Andric       }
7078480093f4SDimitry Andric     } else {
7079480093f4SDimitry Andric       size_t num_disassembly_lines = GetNumDisassemblyLines();
7080480093f4SDimitry Andric       if (num_disassembly_lines > 0) {
7081480093f4SDimitry Andric         // Display disassembly
7082480093f4SDimitry Andric         BreakpointAddrs bp_file_addrs;
7083480093f4SDimitry Andric         Target *target = exe_ctx.GetTargetPtr();
7084480093f4SDimitry Andric         if (target) {
7085480093f4SDimitry Andric           BreakpointList &bp_list = target->GetBreakpointList();
7086480093f4SDimitry Andric           const size_t num_bps = bp_list.GetSize();
7087480093f4SDimitry Andric           for (size_t bp_idx = 0; bp_idx < num_bps; ++bp_idx) {
7088480093f4SDimitry Andric             BreakpointSP bp_sp = bp_list.GetBreakpointAtIndex(bp_idx);
7089480093f4SDimitry Andric             const size_t num_bps_locs = bp_sp->GetNumLocations();
7090480093f4SDimitry Andric             for (size_t bp_loc_idx = 0; bp_loc_idx < num_bps_locs;
7091480093f4SDimitry Andric                  ++bp_loc_idx) {
7092480093f4SDimitry Andric               BreakpointLocationSP bp_loc_sp =
7093480093f4SDimitry Andric                   bp_sp->GetLocationAtIndex(bp_loc_idx);
7094480093f4SDimitry Andric               LineEntry bp_loc_line_entry;
7095480093f4SDimitry Andric               const lldb::addr_t file_addr =
7096480093f4SDimitry Andric                   bp_loc_sp->GetAddress().GetFileAddress();
7097480093f4SDimitry Andric               if (file_addr != LLDB_INVALID_ADDRESS) {
7098480093f4SDimitry Andric                 if (m_disassembly_range.ContainsFileAddress(file_addr))
7099480093f4SDimitry Andric                   bp_file_addrs.insert(file_addr);
7100480093f4SDimitry Andric               }
7101480093f4SDimitry Andric             }
7102480093f4SDimitry Andric           }
7103480093f4SDimitry Andric         }
7104480093f4SDimitry Andric 
7105480093f4SDimitry Andric         const attr_t selected_highlight_attr = A_REVERSE;
7106e8d8bef9SDimitry Andric         const attr_t pc_highlight_attr = COLOR_PAIR(WhiteOnBlue);
7107480093f4SDimitry Andric 
7108480093f4SDimitry Andric         StreamString strm;
7109480093f4SDimitry Andric 
7110480093f4SDimitry Andric         InstructionList &insts = m_disassembly_sp->GetInstructionList();
7111480093f4SDimitry Andric         Address pc_address;
7112480093f4SDimitry Andric 
7113480093f4SDimitry Andric         if (frame_sp)
7114480093f4SDimitry Andric           pc_address = frame_sp->GetFrameCodeAddress();
7115480093f4SDimitry Andric         const uint32_t pc_idx =
7116480093f4SDimitry Andric             pc_address.IsValid()
7117480093f4SDimitry Andric                 ? insts.GetIndexOfInstructionAtAddress(pc_address)
7118480093f4SDimitry Andric                 : UINT32_MAX;
7119480093f4SDimitry Andric         if (set_selected_line_to_pc) {
7120480093f4SDimitry Andric           m_selected_line = pc_idx;
7121480093f4SDimitry Andric         }
7122480093f4SDimitry Andric 
7123480093f4SDimitry Andric         const uint32_t non_visible_pc_offset = (num_visible_lines / 5);
7124480093f4SDimitry Andric         if (static_cast<size_t>(m_first_visible_line) >= num_disassembly_lines)
7125480093f4SDimitry Andric           m_first_visible_line = 0;
7126480093f4SDimitry Andric 
7127480093f4SDimitry Andric         if (pc_idx < num_disassembly_lines) {
7128480093f4SDimitry Andric           if (pc_idx < static_cast<uint32_t>(m_first_visible_line) ||
7129480093f4SDimitry Andric               pc_idx >= m_first_visible_line + num_visible_lines)
7130480093f4SDimitry Andric             m_first_visible_line = pc_idx - non_visible_pc_offset;
7131480093f4SDimitry Andric         }
7132480093f4SDimitry Andric 
7133480093f4SDimitry Andric         for (size_t i = 0; i < num_visible_lines; ++i) {
7134480093f4SDimitry Andric           const uint32_t inst_idx = m_first_visible_line + i;
7135480093f4SDimitry Andric           Instruction *inst = insts.GetInstructionAtIndex(inst_idx).get();
7136480093f4SDimitry Andric           if (!inst)
7137480093f4SDimitry Andric             break;
7138480093f4SDimitry Andric 
7139480093f4SDimitry Andric           const int line_y = m_min_y + i;
7140480093f4SDimitry Andric           window.MoveCursor(1, line_y);
7141480093f4SDimitry Andric           const bool is_pc_line = frame_sp && inst_idx == pc_idx;
7142480093f4SDimitry Andric           const bool line_is_selected = m_selected_line == inst_idx;
7143349cc55cSDimitry Andric           // Highlight the line as the PC line first, then if the selected
7144349cc55cSDimitry Andric           // line isn't the same as the PC line, highlight it differently
7145480093f4SDimitry Andric           attr_t highlight_attr = 0;
7146480093f4SDimitry Andric           attr_t bp_attr = 0;
7147480093f4SDimitry Andric           if (is_pc_line)
7148480093f4SDimitry Andric             highlight_attr = pc_highlight_attr;
7149480093f4SDimitry Andric           else if (line_is_selected)
7150480093f4SDimitry Andric             highlight_attr = selected_highlight_attr;
7151480093f4SDimitry Andric 
7152480093f4SDimitry Andric           if (bp_file_addrs.find(inst->GetAddress().GetFileAddress()) !=
7153480093f4SDimitry Andric               bp_file_addrs.end())
7154e8d8bef9SDimitry Andric             bp_attr = COLOR_PAIR(BlackOnWhite);
7155480093f4SDimitry Andric 
7156480093f4SDimitry Andric           if (bp_attr)
7157480093f4SDimitry Andric             window.AttributeOn(bp_attr);
7158480093f4SDimitry Andric 
7159480093f4SDimitry Andric           window.Printf(" 0x%16.16llx ",
7160480093f4SDimitry Andric                         static_cast<unsigned long long>(
7161480093f4SDimitry Andric                             inst->GetAddress().GetLoadAddress(target)));
7162480093f4SDimitry Andric 
7163480093f4SDimitry Andric           if (bp_attr)
7164480093f4SDimitry Andric             window.AttributeOff(bp_attr);
7165480093f4SDimitry Andric 
7166480093f4SDimitry Andric           window.PutChar(ACS_VLINE);
7167480093f4SDimitry Andric           // Mark the line with the PC with a diamond
7168480093f4SDimitry Andric           if (is_pc_line)
7169480093f4SDimitry Andric             window.PutChar(ACS_DIAMOND);
7170480093f4SDimitry Andric           else
7171480093f4SDimitry Andric             window.PutChar(' ');
7172480093f4SDimitry Andric 
7173480093f4SDimitry Andric           if (highlight_attr)
7174480093f4SDimitry Andric             window.AttributeOn(highlight_attr);
7175480093f4SDimitry Andric 
7176480093f4SDimitry Andric           const char *mnemonic = inst->GetMnemonic(&exe_ctx);
7177480093f4SDimitry Andric           const char *operands = inst->GetOperands(&exe_ctx);
7178480093f4SDimitry Andric           const char *comment = inst->GetComment(&exe_ctx);
7179480093f4SDimitry Andric 
7180480093f4SDimitry Andric           if (mnemonic != nullptr && mnemonic[0] == '\0')
7181480093f4SDimitry Andric             mnemonic = nullptr;
7182480093f4SDimitry Andric           if (operands != nullptr && operands[0] == '\0')
7183480093f4SDimitry Andric             operands = nullptr;
7184480093f4SDimitry Andric           if (comment != nullptr && comment[0] == '\0')
7185480093f4SDimitry Andric             comment = nullptr;
7186480093f4SDimitry Andric 
7187480093f4SDimitry Andric           strm.Clear();
7188480093f4SDimitry Andric 
7189480093f4SDimitry Andric           if (mnemonic != nullptr && operands != nullptr && comment != nullptr)
7190480093f4SDimitry Andric             strm.Printf("%-8s %-25s ; %s", mnemonic, operands, comment);
7191480093f4SDimitry Andric           else if (mnemonic != nullptr && operands != nullptr)
7192480093f4SDimitry Andric             strm.Printf("%-8s %s", mnemonic, operands);
7193480093f4SDimitry Andric           else if (mnemonic != nullptr)
7194480093f4SDimitry Andric             strm.Printf("%s", mnemonic);
7195480093f4SDimitry Andric 
7196480093f4SDimitry Andric           int right_pad = 1;
7197e8d8bef9SDimitry Andric           window.PutCStringTruncated(
7198e8d8bef9SDimitry Andric               right_pad,
7199e8d8bef9SDimitry Andric               strm.GetString().substr(m_first_visible_column).data());
7200480093f4SDimitry Andric 
7201480093f4SDimitry Andric           if (is_pc_line && frame_sp &&
7202480093f4SDimitry Andric               frame_sp->GetConcreteFrameIndex() == 0) {
7203480093f4SDimitry Andric             StopInfoSP stop_info_sp;
7204480093f4SDimitry Andric             if (thread)
7205480093f4SDimitry Andric               stop_info_sp = thread->GetStopInfo();
7206480093f4SDimitry Andric             if (stop_info_sp) {
7207480093f4SDimitry Andric               const char *stop_description = stop_info_sp->GetDescription();
7208480093f4SDimitry Andric               if (stop_description && stop_description[0]) {
7209480093f4SDimitry Andric                 size_t stop_description_len = strlen(stop_description);
7210480093f4SDimitry Andric                 int desc_x = window_width - stop_description_len - 16;
7211e8d8bef9SDimitry Andric                 if (desc_x - window.GetCursorX() > 0)
7212480093f4SDimitry Andric                   window.Printf("%*s", desc_x - window.GetCursorX(), "");
7213e8d8bef9SDimitry Andric                 window.MoveCursor(window_width - stop_description_len - 15,
7214e8d8bef9SDimitry Andric                                   line_y);
7215bdd1243dSDimitry Andric                 if (thread)
7216e8d8bef9SDimitry Andric                   window.PrintfTruncated(1, "<<< Thread %u: %s ",
7217bdd1243dSDimitry Andric                                          thread->GetIndexID(),
7218bdd1243dSDimitry Andric                                          stop_description);
7219480093f4SDimitry Andric               }
7220480093f4SDimitry Andric             } else {
7221480093f4SDimitry Andric               window.Printf("%*s", window_width - window.GetCursorX() - 1, "");
7222480093f4SDimitry Andric             }
7223480093f4SDimitry Andric           }
7224480093f4SDimitry Andric           if (highlight_attr)
7225480093f4SDimitry Andric             window.AttributeOff(highlight_attr);
7226480093f4SDimitry Andric         }
7227480093f4SDimitry Andric       }
7228480093f4SDimitry Andric     }
7229480093f4SDimitry Andric     return true; // Drawing handled
7230480093f4SDimitry Andric   }
7231480093f4SDimitry Andric 
7232480093f4SDimitry Andric   size_t GetNumLines() {
7233480093f4SDimitry Andric     size_t num_lines = GetNumSourceLines();
7234480093f4SDimitry Andric     if (num_lines == 0)
7235480093f4SDimitry Andric       num_lines = GetNumDisassemblyLines();
7236480093f4SDimitry Andric     return num_lines;
7237480093f4SDimitry Andric   }
7238480093f4SDimitry Andric 
7239480093f4SDimitry Andric   size_t GetNumSourceLines() const {
7240480093f4SDimitry Andric     if (m_file_sp)
7241480093f4SDimitry Andric       return m_file_sp->GetNumLines();
7242480093f4SDimitry Andric     return 0;
7243480093f4SDimitry Andric   }
7244480093f4SDimitry Andric 
7245480093f4SDimitry Andric   size_t GetNumDisassemblyLines() const {
7246480093f4SDimitry Andric     if (m_disassembly_sp)
7247480093f4SDimitry Andric       return m_disassembly_sp->GetInstructionList().GetSize();
7248480093f4SDimitry Andric     return 0;
7249480093f4SDimitry Andric   }
7250480093f4SDimitry Andric 
7251480093f4SDimitry Andric   HandleCharResult WindowDelegateHandleChar(Window &window, int c) override {
7252480093f4SDimitry Andric     const uint32_t num_visible_lines = NumVisibleLines();
7253480093f4SDimitry Andric     const size_t num_lines = GetNumLines();
7254480093f4SDimitry Andric 
7255480093f4SDimitry Andric     switch (c) {
7256480093f4SDimitry Andric     case ',':
7257480093f4SDimitry Andric     case KEY_PPAGE:
7258480093f4SDimitry Andric       // Page up key
7259480093f4SDimitry Andric       if (static_cast<uint32_t>(m_first_visible_line) > num_visible_lines)
7260480093f4SDimitry Andric         m_first_visible_line -= num_visible_lines;
7261480093f4SDimitry Andric       else
7262480093f4SDimitry Andric         m_first_visible_line = 0;
7263480093f4SDimitry Andric       m_selected_line = m_first_visible_line;
7264480093f4SDimitry Andric       return eKeyHandled;
7265480093f4SDimitry Andric 
7266480093f4SDimitry Andric     case '.':
7267480093f4SDimitry Andric     case KEY_NPAGE:
7268480093f4SDimitry Andric       // Page down key
7269480093f4SDimitry Andric       {
7270480093f4SDimitry Andric         if (m_first_visible_line + num_visible_lines < num_lines)
7271480093f4SDimitry Andric           m_first_visible_line += num_visible_lines;
7272480093f4SDimitry Andric         else if (num_lines < num_visible_lines)
7273480093f4SDimitry Andric           m_first_visible_line = 0;
7274480093f4SDimitry Andric         else
7275480093f4SDimitry Andric           m_first_visible_line = num_lines - num_visible_lines;
7276480093f4SDimitry Andric         m_selected_line = m_first_visible_line;
7277480093f4SDimitry Andric       }
7278480093f4SDimitry Andric       return eKeyHandled;
7279480093f4SDimitry Andric 
7280480093f4SDimitry Andric     case KEY_UP:
7281480093f4SDimitry Andric       if (m_selected_line > 0) {
7282480093f4SDimitry Andric         m_selected_line--;
7283480093f4SDimitry Andric         if (static_cast<size_t>(m_first_visible_line) > m_selected_line)
7284480093f4SDimitry Andric           m_first_visible_line = m_selected_line;
7285480093f4SDimitry Andric       }
7286480093f4SDimitry Andric       return eKeyHandled;
7287480093f4SDimitry Andric 
7288480093f4SDimitry Andric     case KEY_DOWN:
7289480093f4SDimitry Andric       if (m_selected_line + 1 < num_lines) {
7290480093f4SDimitry Andric         m_selected_line++;
7291480093f4SDimitry Andric         if (m_first_visible_line + num_visible_lines < m_selected_line)
7292480093f4SDimitry Andric           m_first_visible_line++;
7293480093f4SDimitry Andric       }
7294480093f4SDimitry Andric       return eKeyHandled;
7295480093f4SDimitry Andric 
7296e8d8bef9SDimitry Andric     case KEY_LEFT:
7297e8d8bef9SDimitry Andric       if (m_first_visible_column > 0)
7298e8d8bef9SDimitry Andric         --m_first_visible_column;
7299e8d8bef9SDimitry Andric       return eKeyHandled;
7300e8d8bef9SDimitry Andric 
7301e8d8bef9SDimitry Andric     case KEY_RIGHT:
7302e8d8bef9SDimitry Andric       ++m_first_visible_column;
7303e8d8bef9SDimitry Andric       return eKeyHandled;
7304e8d8bef9SDimitry Andric 
7305480093f4SDimitry Andric     case '\r':
7306480093f4SDimitry Andric     case '\n':
7307480093f4SDimitry Andric     case KEY_ENTER:
7308480093f4SDimitry Andric       // Set a breakpoint and run to the line using a one shot breakpoint
7309480093f4SDimitry Andric       if (GetNumSourceLines() > 0) {
7310480093f4SDimitry Andric         ExecutionContext exe_ctx =
7311480093f4SDimitry Andric             m_debugger.GetCommandInterpreter().GetExecutionContext();
7312480093f4SDimitry Andric         if (exe_ctx.HasProcessScope() && exe_ctx.GetProcessRef().IsAlive()) {
7313480093f4SDimitry Andric           BreakpointSP bp_sp = exe_ctx.GetTargetRef().CreateBreakpoint(
7314480093f4SDimitry Andric               nullptr, // Don't limit the breakpoint to certain modules
7315480093f4SDimitry Andric               m_file_sp->GetFileSpec(), // Source file
7316480093f4SDimitry Andric               m_selected_line +
7317480093f4SDimitry Andric                   1, // Source line number (m_selected_line is zero based)
7318480093f4SDimitry Andric               0,     // Unspecified column.
7319480093f4SDimitry Andric               0,     // No offset
7320480093f4SDimitry Andric               eLazyBoolCalculate,  // Check inlines using global setting
7321480093f4SDimitry Andric               eLazyBoolCalculate,  // Skip prologue using global setting,
7322480093f4SDimitry Andric               false,               // internal
7323480093f4SDimitry Andric               false,               // request_hardware
7324480093f4SDimitry Andric               eLazyBoolCalculate); // move_to_nearest_code
7325480093f4SDimitry Andric           // Make breakpoint one shot
7326fe6060f1SDimitry Andric           bp_sp->GetOptions().SetOneShot(true);
7327480093f4SDimitry Andric           exe_ctx.GetProcessRef().Resume();
7328480093f4SDimitry Andric         }
7329480093f4SDimitry Andric       } else if (m_selected_line < GetNumDisassemblyLines()) {
7330480093f4SDimitry Andric         const Instruction *inst = m_disassembly_sp->GetInstructionList()
7331480093f4SDimitry Andric                                       .GetInstructionAtIndex(m_selected_line)
7332480093f4SDimitry Andric                                       .get();
7333480093f4SDimitry Andric         ExecutionContext exe_ctx =
7334480093f4SDimitry Andric             m_debugger.GetCommandInterpreter().GetExecutionContext();
7335480093f4SDimitry Andric         if (exe_ctx.HasTargetScope()) {
7336480093f4SDimitry Andric           Address addr = inst->GetAddress();
7337480093f4SDimitry Andric           BreakpointSP bp_sp = exe_ctx.GetTargetRef().CreateBreakpoint(
7338480093f4SDimitry Andric               addr,   // lldb_private::Address
7339480093f4SDimitry Andric               false,  // internal
7340480093f4SDimitry Andric               false); // request_hardware
7341480093f4SDimitry Andric           // Make breakpoint one shot
7342fe6060f1SDimitry Andric           bp_sp->GetOptions().SetOneShot(true);
7343480093f4SDimitry Andric           exe_ctx.GetProcessRef().Resume();
7344480093f4SDimitry Andric         }
7345480093f4SDimitry Andric       }
7346480093f4SDimitry Andric       return eKeyHandled;
7347480093f4SDimitry Andric 
7348480093f4SDimitry Andric     case 'b': // 'b' == toggle breakpoint on currently selected line
7349e8d8bef9SDimitry Andric       ToggleBreakpointOnSelectedLine();
7350480093f4SDimitry Andric       return eKeyHandled;
7351480093f4SDimitry Andric 
7352480093f4SDimitry Andric     case 'D': // 'D' == detach and keep stopped
7353480093f4SDimitry Andric     {
7354480093f4SDimitry Andric       ExecutionContext exe_ctx =
7355480093f4SDimitry Andric           m_debugger.GetCommandInterpreter().GetExecutionContext();
7356480093f4SDimitry Andric       if (exe_ctx.HasProcessScope())
7357e8d8bef9SDimitry Andric         exe_ctx.GetProcessRef().Detach(true);
7358480093f4SDimitry Andric     }
7359480093f4SDimitry Andric       return eKeyHandled;
7360480093f4SDimitry Andric 
7361480093f4SDimitry Andric     case 'c':
7362480093f4SDimitry Andric       // 'c' == continue
7363480093f4SDimitry Andric       {
7364480093f4SDimitry Andric         ExecutionContext exe_ctx =
7365480093f4SDimitry Andric             m_debugger.GetCommandInterpreter().GetExecutionContext();
7366480093f4SDimitry Andric         if (exe_ctx.HasProcessScope())
7367480093f4SDimitry Andric           exe_ctx.GetProcessRef().Resume();
7368480093f4SDimitry Andric       }
7369480093f4SDimitry Andric       return eKeyHandled;
7370480093f4SDimitry Andric 
7371e8d8bef9SDimitry Andric     case 'f':
7372e8d8bef9SDimitry Andric       // 'f' == step out (finish)
7373480093f4SDimitry Andric       {
7374480093f4SDimitry Andric         ExecutionContext exe_ctx =
7375480093f4SDimitry Andric             m_debugger.GetCommandInterpreter().GetExecutionContext();
7376480093f4SDimitry Andric         if (exe_ctx.HasThreadScope() &&
7377480093f4SDimitry Andric             StateIsStoppedState(exe_ctx.GetProcessRef().GetState(), true)) {
737881ad6265SDimitry Andric           Thread *thread = exe_ctx.GetThreadPtr();
7379*06c3fb27SDimitry Andric           uint32_t frame_idx =
7380*06c3fb27SDimitry Andric               thread->GetSelectedFrameIndex(SelectMostRelevantFrame);
738181ad6265SDimitry Andric           exe_ctx.GetThreadRef().StepOut(frame_idx);
7382480093f4SDimitry Andric         }
7383480093f4SDimitry Andric       }
7384480093f4SDimitry Andric       return eKeyHandled;
7385480093f4SDimitry Andric 
7386480093f4SDimitry Andric     case 'n': // 'n' == step over
7387480093f4SDimitry Andric     case 'N': // 'N' == step over instruction
7388480093f4SDimitry Andric     {
7389480093f4SDimitry Andric       ExecutionContext exe_ctx =
7390480093f4SDimitry Andric           m_debugger.GetCommandInterpreter().GetExecutionContext();
7391480093f4SDimitry Andric       if (exe_ctx.HasThreadScope() &&
7392480093f4SDimitry Andric           StateIsStoppedState(exe_ctx.GetProcessRef().GetState(), true)) {
7393480093f4SDimitry Andric         bool source_step = (c == 'n');
7394480093f4SDimitry Andric         exe_ctx.GetThreadRef().StepOver(source_step);
7395480093f4SDimitry Andric       }
7396480093f4SDimitry Andric     }
7397480093f4SDimitry Andric       return eKeyHandled;
7398480093f4SDimitry Andric 
7399480093f4SDimitry Andric     case 's': // 's' == step into
7400480093f4SDimitry Andric     case 'S': // 'S' == step into instruction
7401480093f4SDimitry Andric     {
7402480093f4SDimitry Andric       ExecutionContext exe_ctx =
7403480093f4SDimitry Andric           m_debugger.GetCommandInterpreter().GetExecutionContext();
7404480093f4SDimitry Andric       if (exe_ctx.HasThreadScope() &&
7405480093f4SDimitry Andric           StateIsStoppedState(exe_ctx.GetProcessRef().GetState(), true)) {
7406480093f4SDimitry Andric         bool source_step = (c == 's');
7407480093f4SDimitry Andric         exe_ctx.GetThreadRef().StepIn(source_step);
7408480093f4SDimitry Andric       }
7409480093f4SDimitry Andric     }
7410480093f4SDimitry Andric       return eKeyHandled;
7411480093f4SDimitry Andric 
7412e8d8bef9SDimitry Andric     case 'u': // 'u' == frame up
7413e8d8bef9SDimitry Andric     case 'd': // 'd' == frame down
7414e8d8bef9SDimitry Andric     {
7415e8d8bef9SDimitry Andric       ExecutionContext exe_ctx =
7416e8d8bef9SDimitry Andric           m_debugger.GetCommandInterpreter().GetExecutionContext();
7417e8d8bef9SDimitry Andric       if (exe_ctx.HasThreadScope()) {
7418e8d8bef9SDimitry Andric         Thread *thread = exe_ctx.GetThreadPtr();
7419*06c3fb27SDimitry Andric         uint32_t frame_idx =
7420*06c3fb27SDimitry Andric             thread->GetSelectedFrameIndex(SelectMostRelevantFrame);
7421e8d8bef9SDimitry Andric         if (frame_idx == UINT32_MAX)
7422e8d8bef9SDimitry Andric           frame_idx = 0;
7423e8d8bef9SDimitry Andric         if (c == 'u' && frame_idx + 1 < thread->GetStackFrameCount())
7424e8d8bef9SDimitry Andric           ++frame_idx;
7425e8d8bef9SDimitry Andric         else if (c == 'd' && frame_idx > 0)
7426e8d8bef9SDimitry Andric           --frame_idx;
7427e8d8bef9SDimitry Andric         if (thread->SetSelectedFrameByIndex(frame_idx, true))
7428*06c3fb27SDimitry Andric           exe_ctx.SetFrameSP(thread->GetSelectedFrame(SelectMostRelevantFrame));
7429e8d8bef9SDimitry Andric       }
7430e8d8bef9SDimitry Andric     }
7431e8d8bef9SDimitry Andric       return eKeyHandled;
7432e8d8bef9SDimitry Andric 
7433480093f4SDimitry Andric     case 'h':
7434480093f4SDimitry Andric       window.CreateHelpSubwindow();
7435480093f4SDimitry Andric       return eKeyHandled;
7436480093f4SDimitry Andric 
7437480093f4SDimitry Andric     default:
7438480093f4SDimitry Andric       break;
7439480093f4SDimitry Andric     }
7440480093f4SDimitry Andric     return eKeyNotHandled;
7441480093f4SDimitry Andric   }
7442480093f4SDimitry Andric 
7443e8d8bef9SDimitry Andric   void ToggleBreakpointOnSelectedLine() {
7444e8d8bef9SDimitry Andric     ExecutionContext exe_ctx =
7445e8d8bef9SDimitry Andric         m_debugger.GetCommandInterpreter().GetExecutionContext();
7446e8d8bef9SDimitry Andric     if (!exe_ctx.HasTargetScope())
7447e8d8bef9SDimitry Andric       return;
7448e8d8bef9SDimitry Andric     if (GetNumSourceLines() > 0) {
7449e8d8bef9SDimitry Andric       // Source file breakpoint.
7450e8d8bef9SDimitry Andric       BreakpointList &bp_list = exe_ctx.GetTargetRef().GetBreakpointList();
7451e8d8bef9SDimitry Andric       const size_t num_bps = bp_list.GetSize();
7452e8d8bef9SDimitry Andric       for (size_t bp_idx = 0; bp_idx < num_bps; ++bp_idx) {
7453e8d8bef9SDimitry Andric         BreakpointSP bp_sp = bp_list.GetBreakpointAtIndex(bp_idx);
7454e8d8bef9SDimitry Andric         const size_t num_bps_locs = bp_sp->GetNumLocations();
7455e8d8bef9SDimitry Andric         for (size_t bp_loc_idx = 0; bp_loc_idx < num_bps_locs; ++bp_loc_idx) {
7456e8d8bef9SDimitry Andric           BreakpointLocationSP bp_loc_sp =
7457e8d8bef9SDimitry Andric               bp_sp->GetLocationAtIndex(bp_loc_idx);
7458e8d8bef9SDimitry Andric           LineEntry bp_loc_line_entry;
7459e8d8bef9SDimitry Andric           if (bp_loc_sp->GetAddress().CalculateSymbolContextLineEntry(
7460e8d8bef9SDimitry Andric                   bp_loc_line_entry)) {
7461e8d8bef9SDimitry Andric             if (m_file_sp->GetFileSpec() == bp_loc_line_entry.file &&
7462e8d8bef9SDimitry Andric                 m_selected_line + 1 == bp_loc_line_entry.line) {
7463e8d8bef9SDimitry Andric               bool removed =
7464e8d8bef9SDimitry Andric                   exe_ctx.GetTargetRef().RemoveBreakpointByID(bp_sp->GetID());
7465e8d8bef9SDimitry Andric               assert(removed);
7466e8d8bef9SDimitry Andric               UNUSED_IF_ASSERT_DISABLED(removed);
7467e8d8bef9SDimitry Andric               return; // Existing breakpoint removed.
7468e8d8bef9SDimitry Andric             }
7469e8d8bef9SDimitry Andric           }
7470e8d8bef9SDimitry Andric         }
7471e8d8bef9SDimitry Andric       }
7472e8d8bef9SDimitry Andric       // No breakpoint found on the location, add it.
7473e8d8bef9SDimitry Andric       BreakpointSP bp_sp = exe_ctx.GetTargetRef().CreateBreakpoint(
7474e8d8bef9SDimitry Andric           nullptr, // Don't limit the breakpoint to certain modules
7475e8d8bef9SDimitry Andric           m_file_sp->GetFileSpec(), // Source file
7476e8d8bef9SDimitry Andric           m_selected_line +
7477e8d8bef9SDimitry Andric               1, // Source line number (m_selected_line is zero based)
7478e8d8bef9SDimitry Andric           0,     // No column specified.
7479e8d8bef9SDimitry Andric           0,     // No offset
7480e8d8bef9SDimitry Andric           eLazyBoolCalculate,  // Check inlines using global setting
7481e8d8bef9SDimitry Andric           eLazyBoolCalculate,  // Skip prologue using global setting,
7482e8d8bef9SDimitry Andric           false,               // internal
7483e8d8bef9SDimitry Andric           false,               // request_hardware
7484e8d8bef9SDimitry Andric           eLazyBoolCalculate); // move_to_nearest_code
7485e8d8bef9SDimitry Andric     } else {
7486e8d8bef9SDimitry Andric       // Disassembly breakpoint.
7487e8d8bef9SDimitry Andric       assert(GetNumDisassemblyLines() > 0);
7488e8d8bef9SDimitry Andric       assert(m_selected_line < GetNumDisassemblyLines());
7489e8d8bef9SDimitry Andric       const Instruction *inst = m_disassembly_sp->GetInstructionList()
7490e8d8bef9SDimitry Andric                                     .GetInstructionAtIndex(m_selected_line)
7491e8d8bef9SDimitry Andric                                     .get();
7492e8d8bef9SDimitry Andric       Address addr = inst->GetAddress();
7493e8d8bef9SDimitry Andric       // Try to find it.
7494e8d8bef9SDimitry Andric       BreakpointList &bp_list = exe_ctx.GetTargetRef().GetBreakpointList();
7495e8d8bef9SDimitry Andric       const size_t num_bps = bp_list.GetSize();
7496e8d8bef9SDimitry Andric       for (size_t bp_idx = 0; bp_idx < num_bps; ++bp_idx) {
7497e8d8bef9SDimitry Andric         BreakpointSP bp_sp = bp_list.GetBreakpointAtIndex(bp_idx);
7498e8d8bef9SDimitry Andric         const size_t num_bps_locs = bp_sp->GetNumLocations();
7499e8d8bef9SDimitry Andric         for (size_t bp_loc_idx = 0; bp_loc_idx < num_bps_locs; ++bp_loc_idx) {
7500e8d8bef9SDimitry Andric           BreakpointLocationSP bp_loc_sp =
7501e8d8bef9SDimitry Andric               bp_sp->GetLocationAtIndex(bp_loc_idx);
7502e8d8bef9SDimitry Andric           LineEntry bp_loc_line_entry;
7503e8d8bef9SDimitry Andric           const lldb::addr_t file_addr =
7504e8d8bef9SDimitry Andric               bp_loc_sp->GetAddress().GetFileAddress();
7505e8d8bef9SDimitry Andric           if (file_addr == addr.GetFileAddress()) {
7506e8d8bef9SDimitry Andric             bool removed =
7507e8d8bef9SDimitry Andric                 exe_ctx.GetTargetRef().RemoveBreakpointByID(bp_sp->GetID());
7508e8d8bef9SDimitry Andric             assert(removed);
7509e8d8bef9SDimitry Andric             UNUSED_IF_ASSERT_DISABLED(removed);
7510e8d8bef9SDimitry Andric             return; // Existing breakpoint removed.
7511e8d8bef9SDimitry Andric           }
7512e8d8bef9SDimitry Andric         }
7513e8d8bef9SDimitry Andric       }
7514e8d8bef9SDimitry Andric       // No breakpoint found on the address, add it.
7515e8d8bef9SDimitry Andric       BreakpointSP bp_sp =
7516e8d8bef9SDimitry Andric           exe_ctx.GetTargetRef().CreateBreakpoint(addr, // lldb_private::Address
7517e8d8bef9SDimitry Andric                                                   false,  // internal
7518e8d8bef9SDimitry Andric                                                   false); // request_hardware
7519e8d8bef9SDimitry Andric     }
7520e8d8bef9SDimitry Andric   }
7521e8d8bef9SDimitry Andric 
7522480093f4SDimitry Andric protected:
7523480093f4SDimitry Andric   typedef std::set<uint32_t> BreakpointLines;
7524480093f4SDimitry Andric   typedef std::set<lldb::addr_t> BreakpointAddrs;
7525480093f4SDimitry Andric 
7526480093f4SDimitry Andric   Debugger &m_debugger;
7527480093f4SDimitry Andric   SymbolContext m_sc;
7528480093f4SDimitry Andric   SourceManager::FileSP m_file_sp;
752981ad6265SDimitry Andric   SymbolContextScope *m_disassembly_scope = nullptr;
7530480093f4SDimitry Andric   lldb::DisassemblerSP m_disassembly_sp;
7531480093f4SDimitry Andric   AddressRange m_disassembly_range;
7532480093f4SDimitry Andric   StreamString m_title;
753381ad6265SDimitry Andric   lldb::user_id_t m_tid = LLDB_INVALID_THREAD_ID;
753481ad6265SDimitry Andric   int m_line_width = 4;
753581ad6265SDimitry Andric   uint32_t m_selected_line = 0; // The selected line
753681ad6265SDimitry Andric   uint32_t m_pc_line = 0;       // The line with the PC
753781ad6265SDimitry Andric   uint32_t m_stop_id = 0;
753881ad6265SDimitry Andric   uint32_t m_frame_idx = UINT32_MAX;
753981ad6265SDimitry Andric   int m_first_visible_line = 0;
754081ad6265SDimitry Andric   int m_first_visible_column = 0;
754181ad6265SDimitry Andric   int m_min_x = 0;
754281ad6265SDimitry Andric   int m_min_y = 0;
754381ad6265SDimitry Andric   int m_max_x = 0;
754481ad6265SDimitry Andric   int m_max_y = 0;
7545480093f4SDimitry Andric };
7546480093f4SDimitry Andric 
7547480093f4SDimitry Andric DisplayOptions ValueObjectListDelegate::g_options = {true};
7548480093f4SDimitry Andric 
7549480093f4SDimitry Andric IOHandlerCursesGUI::IOHandlerCursesGUI(Debugger &debugger)
7550480093f4SDimitry Andric     : IOHandler(debugger, IOHandler::Type::Curses) {}
7551480093f4SDimitry Andric 
7552480093f4SDimitry Andric void IOHandlerCursesGUI::Activate() {
7553480093f4SDimitry Andric   IOHandler::Activate();
7554480093f4SDimitry Andric   if (!m_app_ap) {
75555ffd83dbSDimitry Andric     m_app_ap = std::make_unique<Application>(GetInputFILE(), GetOutputFILE());
7556480093f4SDimitry Andric 
7557480093f4SDimitry Andric     // This is both a window and a menu delegate
7558480093f4SDimitry Andric     std::shared_ptr<ApplicationDelegate> app_delegate_sp(
7559480093f4SDimitry Andric         new ApplicationDelegate(*m_app_ap, m_debugger));
7560480093f4SDimitry Andric 
7561480093f4SDimitry Andric     MenuDelegateSP app_menu_delegate_sp =
7562480093f4SDimitry Andric         std::static_pointer_cast<MenuDelegate>(app_delegate_sp);
7563480093f4SDimitry Andric     MenuSP lldb_menu_sp(
7564480093f4SDimitry Andric         new Menu("LLDB", "F1", KEY_F(1), ApplicationDelegate::eMenuID_LLDB));
7565480093f4SDimitry Andric     MenuSP exit_menuitem_sp(
7566480093f4SDimitry Andric         new Menu("Exit", nullptr, 'x', ApplicationDelegate::eMenuID_LLDBExit));
7567480093f4SDimitry Andric     exit_menuitem_sp->SetCannedResult(MenuActionResult::Quit);
7568480093f4SDimitry Andric     lldb_menu_sp->AddSubmenu(MenuSP(new Menu(
7569480093f4SDimitry Andric         "About LLDB", nullptr, 'a', ApplicationDelegate::eMenuID_LLDBAbout)));
7570480093f4SDimitry Andric     lldb_menu_sp->AddSubmenu(MenuSP(new Menu(Menu::Type::Separator)));
7571480093f4SDimitry Andric     lldb_menu_sp->AddSubmenu(exit_menuitem_sp);
7572480093f4SDimitry Andric 
7573480093f4SDimitry Andric     MenuSP target_menu_sp(new Menu("Target", "F2", KEY_F(2),
7574480093f4SDimitry Andric                                    ApplicationDelegate::eMenuID_Target));
7575480093f4SDimitry Andric     target_menu_sp->AddSubmenu(MenuSP(new Menu(
7576480093f4SDimitry Andric         "Create", nullptr, 'c', ApplicationDelegate::eMenuID_TargetCreate)));
7577480093f4SDimitry Andric     target_menu_sp->AddSubmenu(MenuSP(new Menu(
7578480093f4SDimitry Andric         "Delete", nullptr, 'd', ApplicationDelegate::eMenuID_TargetDelete)));
7579480093f4SDimitry Andric 
7580480093f4SDimitry Andric     MenuSP process_menu_sp(new Menu("Process", "F3", KEY_F(3),
7581480093f4SDimitry Andric                                     ApplicationDelegate::eMenuID_Process));
7582480093f4SDimitry Andric     process_menu_sp->AddSubmenu(MenuSP(new Menu(
7583480093f4SDimitry Andric         "Attach", nullptr, 'a', ApplicationDelegate::eMenuID_ProcessAttach)));
7584e8d8bef9SDimitry Andric     process_menu_sp->AddSubmenu(
7585e8d8bef9SDimitry Andric         MenuSP(new Menu("Detach and resume", nullptr, 'd',
7586e8d8bef9SDimitry Andric                         ApplicationDelegate::eMenuID_ProcessDetachResume)));
7587e8d8bef9SDimitry Andric     process_menu_sp->AddSubmenu(
7588e8d8bef9SDimitry Andric         MenuSP(new Menu("Detach suspended", nullptr, 's',
7589e8d8bef9SDimitry Andric                         ApplicationDelegate::eMenuID_ProcessDetachSuspended)));
7590480093f4SDimitry Andric     process_menu_sp->AddSubmenu(MenuSP(new Menu(
7591480093f4SDimitry Andric         "Launch", nullptr, 'l', ApplicationDelegate::eMenuID_ProcessLaunch)));
7592480093f4SDimitry Andric     process_menu_sp->AddSubmenu(MenuSP(new Menu(Menu::Type::Separator)));
7593480093f4SDimitry Andric     process_menu_sp->AddSubmenu(
7594480093f4SDimitry Andric         MenuSP(new Menu("Continue", nullptr, 'c',
7595480093f4SDimitry Andric                         ApplicationDelegate::eMenuID_ProcessContinue)));
7596480093f4SDimitry Andric     process_menu_sp->AddSubmenu(MenuSP(new Menu(
7597480093f4SDimitry Andric         "Halt", nullptr, 'h', ApplicationDelegate::eMenuID_ProcessHalt)));
7598480093f4SDimitry Andric     process_menu_sp->AddSubmenu(MenuSP(new Menu(
7599480093f4SDimitry Andric         "Kill", nullptr, 'k', ApplicationDelegate::eMenuID_ProcessKill)));
7600480093f4SDimitry Andric 
7601480093f4SDimitry Andric     MenuSP thread_menu_sp(new Menu("Thread", "F4", KEY_F(4),
7602480093f4SDimitry Andric                                    ApplicationDelegate::eMenuID_Thread));
7603480093f4SDimitry Andric     thread_menu_sp->AddSubmenu(MenuSP(new Menu(
7604480093f4SDimitry Andric         "Step In", nullptr, 'i', ApplicationDelegate::eMenuID_ThreadStepIn)));
7605480093f4SDimitry Andric     thread_menu_sp->AddSubmenu(
7606480093f4SDimitry Andric         MenuSP(new Menu("Step Over", nullptr, 'v',
7607480093f4SDimitry Andric                         ApplicationDelegate::eMenuID_ThreadStepOver)));
7608480093f4SDimitry Andric     thread_menu_sp->AddSubmenu(MenuSP(new Menu(
7609480093f4SDimitry Andric         "Step Out", nullptr, 'o', ApplicationDelegate::eMenuID_ThreadStepOut)));
7610480093f4SDimitry Andric 
7611480093f4SDimitry Andric     MenuSP view_menu_sp(
7612480093f4SDimitry Andric         new Menu("View", "F5", KEY_F(5), ApplicationDelegate::eMenuID_View));
7613480093f4SDimitry Andric     view_menu_sp->AddSubmenu(
7614349cc55cSDimitry Andric         MenuSP(new Menu("Backtrace", nullptr, 't',
7615480093f4SDimitry Andric                         ApplicationDelegate::eMenuID_ViewBacktrace)));
7616480093f4SDimitry Andric     view_menu_sp->AddSubmenu(
7617480093f4SDimitry Andric         MenuSP(new Menu("Registers", nullptr, 'r',
7618480093f4SDimitry Andric                         ApplicationDelegate::eMenuID_ViewRegisters)));
7619480093f4SDimitry Andric     view_menu_sp->AddSubmenu(MenuSP(new Menu(
7620480093f4SDimitry Andric         "Source", nullptr, 's', ApplicationDelegate::eMenuID_ViewSource)));
7621480093f4SDimitry Andric     view_menu_sp->AddSubmenu(
7622480093f4SDimitry Andric         MenuSP(new Menu("Variables", nullptr, 'v',
7623480093f4SDimitry Andric                         ApplicationDelegate::eMenuID_ViewVariables)));
7624349cc55cSDimitry Andric     view_menu_sp->AddSubmenu(
7625349cc55cSDimitry Andric         MenuSP(new Menu("Breakpoints", nullptr, 'b',
7626349cc55cSDimitry Andric                         ApplicationDelegate::eMenuID_ViewBreakpoints)));
7627480093f4SDimitry Andric 
7628480093f4SDimitry Andric     MenuSP help_menu_sp(
7629480093f4SDimitry Andric         new Menu("Help", "F6", KEY_F(6), ApplicationDelegate::eMenuID_Help));
7630480093f4SDimitry Andric     help_menu_sp->AddSubmenu(MenuSP(new Menu(
7631480093f4SDimitry Andric         "GUI Help", nullptr, 'g', ApplicationDelegate::eMenuID_HelpGUIHelp)));
7632480093f4SDimitry Andric 
7633480093f4SDimitry Andric     m_app_ap->Initialize();
7634480093f4SDimitry Andric     WindowSP &main_window_sp = m_app_ap->GetMainWindow();
7635480093f4SDimitry Andric 
7636480093f4SDimitry Andric     MenuSP menubar_sp(new Menu(Menu::Type::Bar));
7637480093f4SDimitry Andric     menubar_sp->AddSubmenu(lldb_menu_sp);
7638480093f4SDimitry Andric     menubar_sp->AddSubmenu(target_menu_sp);
7639480093f4SDimitry Andric     menubar_sp->AddSubmenu(process_menu_sp);
7640480093f4SDimitry Andric     menubar_sp->AddSubmenu(thread_menu_sp);
7641480093f4SDimitry Andric     menubar_sp->AddSubmenu(view_menu_sp);
7642480093f4SDimitry Andric     menubar_sp->AddSubmenu(help_menu_sp);
7643480093f4SDimitry Andric     menubar_sp->SetDelegate(app_menu_delegate_sp);
7644480093f4SDimitry Andric 
7645480093f4SDimitry Andric     Rect content_bounds = main_window_sp->GetFrame();
7646480093f4SDimitry Andric     Rect menubar_bounds = content_bounds.MakeMenuBar();
7647480093f4SDimitry Andric     Rect status_bounds = content_bounds.MakeStatusBar();
7648480093f4SDimitry Andric     Rect source_bounds;
7649480093f4SDimitry Andric     Rect variables_bounds;
7650480093f4SDimitry Andric     Rect threads_bounds;
7651480093f4SDimitry Andric     Rect source_variables_bounds;
7652480093f4SDimitry Andric     content_bounds.VerticalSplitPercentage(0.80, source_variables_bounds,
7653480093f4SDimitry Andric                                            threads_bounds);
7654480093f4SDimitry Andric     source_variables_bounds.HorizontalSplitPercentage(0.70, source_bounds,
7655480093f4SDimitry Andric                                                       variables_bounds);
7656480093f4SDimitry Andric 
7657480093f4SDimitry Andric     WindowSP menubar_window_sp =
7658480093f4SDimitry Andric         main_window_sp->CreateSubWindow("Menubar", menubar_bounds, false);
7659480093f4SDimitry Andric     // Let the menubar get keys if the active window doesn't handle the keys
7660480093f4SDimitry Andric     // that are typed so it can respond to menubar key presses.
7661480093f4SDimitry Andric     menubar_window_sp->SetCanBeActive(
7662480093f4SDimitry Andric         false); // Don't let the menubar become the active window
7663480093f4SDimitry Andric     menubar_window_sp->SetDelegate(menubar_sp);
7664480093f4SDimitry Andric 
7665480093f4SDimitry Andric     WindowSP source_window_sp(
7666480093f4SDimitry Andric         main_window_sp->CreateSubWindow("Source", source_bounds, true));
7667480093f4SDimitry Andric     WindowSP variables_window_sp(
7668480093f4SDimitry Andric         main_window_sp->CreateSubWindow("Variables", variables_bounds, false));
7669480093f4SDimitry Andric     WindowSP threads_window_sp(
7670480093f4SDimitry Andric         main_window_sp->CreateSubWindow("Threads", threads_bounds, false));
7671480093f4SDimitry Andric     WindowSP status_window_sp(
7672480093f4SDimitry Andric         main_window_sp->CreateSubWindow("Status", status_bounds, false));
7673480093f4SDimitry Andric     status_window_sp->SetCanBeActive(
7674480093f4SDimitry Andric         false); // Don't let the status bar become the active window
7675480093f4SDimitry Andric     main_window_sp->SetDelegate(
7676480093f4SDimitry Andric         std::static_pointer_cast<WindowDelegate>(app_delegate_sp));
7677480093f4SDimitry Andric     source_window_sp->SetDelegate(
7678480093f4SDimitry Andric         WindowDelegateSP(new SourceFileWindowDelegate(m_debugger)));
7679480093f4SDimitry Andric     variables_window_sp->SetDelegate(
7680480093f4SDimitry Andric         WindowDelegateSP(new FrameVariablesWindowDelegate(m_debugger)));
7681480093f4SDimitry Andric     TreeDelegateSP thread_delegate_sp(new ThreadsTreeDelegate(m_debugger));
7682480093f4SDimitry Andric     threads_window_sp->SetDelegate(WindowDelegateSP(
7683480093f4SDimitry Andric         new TreeWindowDelegate(m_debugger, thread_delegate_sp)));
7684480093f4SDimitry Andric     status_window_sp->SetDelegate(
7685480093f4SDimitry Andric         WindowDelegateSP(new StatusBarWindowDelegate(m_debugger)));
7686480093f4SDimitry Andric 
7687e8d8bef9SDimitry Andric     // All colors with black background.
7688e8d8bef9SDimitry Andric     init_pair(1, COLOR_BLACK, COLOR_BLACK);
7689e8d8bef9SDimitry Andric     init_pair(2, COLOR_RED, COLOR_BLACK);
7690e8d8bef9SDimitry Andric     init_pair(3, COLOR_GREEN, COLOR_BLACK);
7691e8d8bef9SDimitry Andric     init_pair(4, COLOR_YELLOW, COLOR_BLACK);
7692e8d8bef9SDimitry Andric     init_pair(5, COLOR_BLUE, COLOR_BLACK);
7693e8d8bef9SDimitry Andric     init_pair(6, COLOR_MAGENTA, COLOR_BLACK);
7694e8d8bef9SDimitry Andric     init_pair(7, COLOR_CYAN, COLOR_BLACK);
7695e8d8bef9SDimitry Andric     init_pair(8, COLOR_WHITE, COLOR_BLACK);
7696e8d8bef9SDimitry Andric     // All colors with blue background.
7697e8d8bef9SDimitry Andric     init_pair(9, COLOR_BLACK, COLOR_BLUE);
7698e8d8bef9SDimitry Andric     init_pair(10, COLOR_RED, COLOR_BLUE);
7699e8d8bef9SDimitry Andric     init_pair(11, COLOR_GREEN, COLOR_BLUE);
7700e8d8bef9SDimitry Andric     init_pair(12, COLOR_YELLOW, COLOR_BLUE);
7701e8d8bef9SDimitry Andric     init_pair(13, COLOR_BLUE, COLOR_BLUE);
7702e8d8bef9SDimitry Andric     init_pair(14, COLOR_MAGENTA, COLOR_BLUE);
7703e8d8bef9SDimitry Andric     init_pair(15, COLOR_CYAN, COLOR_BLUE);
7704e8d8bef9SDimitry Andric     init_pair(16, COLOR_WHITE, COLOR_BLUE);
7705e8d8bef9SDimitry Andric     // These must match the order in the color indexes enum.
7706e8d8bef9SDimitry Andric     init_pair(17, COLOR_BLACK, COLOR_WHITE);
7707e8d8bef9SDimitry Andric     init_pair(18, COLOR_MAGENTA, COLOR_WHITE);
7708e8d8bef9SDimitry Andric     static_assert(LastColorPairIndex == 18, "Color indexes do not match.");
7709fe6060f1SDimitry Andric 
7710fe6060f1SDimitry Andric     define_key("\033[Z", KEY_SHIFT_TAB);
7711349cc55cSDimitry Andric     define_key("\033\015", KEY_ALT_ENTER);
7712480093f4SDimitry Andric   }
7713480093f4SDimitry Andric }
7714480093f4SDimitry Andric 
7715480093f4SDimitry Andric void IOHandlerCursesGUI::Deactivate() { m_app_ap->Terminate(); }
7716480093f4SDimitry Andric 
7717480093f4SDimitry Andric void IOHandlerCursesGUI::Run() {
7718480093f4SDimitry Andric   m_app_ap->Run(m_debugger);
7719480093f4SDimitry Andric   SetIsDone(true);
7720480093f4SDimitry Andric }
7721480093f4SDimitry Andric 
7722480093f4SDimitry Andric IOHandlerCursesGUI::~IOHandlerCursesGUI() = default;
7723480093f4SDimitry Andric 
7724480093f4SDimitry Andric void IOHandlerCursesGUI::Cancel() {}
7725480093f4SDimitry Andric 
772681ad6265SDimitry Andric bool IOHandlerCursesGUI::Interrupt() {
772781ad6265SDimitry Andric   return m_debugger.GetCommandInterpreter().IOHandlerInterrupt(*this);
772881ad6265SDimitry Andric }
7729480093f4SDimitry Andric 
7730480093f4SDimitry Andric void IOHandlerCursesGUI::GotEOF() {}
7731480093f4SDimitry Andric 
7732e8d8bef9SDimitry Andric void IOHandlerCursesGUI::TerminalSizeChanged() {
7733e8d8bef9SDimitry Andric   m_app_ap->TerminalSizeChanged();
7734e8d8bef9SDimitry Andric }
7735e8d8bef9SDimitry Andric 
7736480093f4SDimitry Andric #endif // LLDB_ENABLE_CURSES
7737